diff options
Diffstat (limited to 'object_carver/carver_operator.py')
-rw-r--r-- | object_carver/carver_operator.py | 2620 |
1 files changed, 1310 insertions, 1310 deletions
diff --git a/object_carver/carver_operator.py b/object_carver/carver_operator.py index c75a5ab7..95fa4af0 100644 --- a/object_carver/carver_operator.py +++ b/object_carver/carver_operator.py @@ -2,340 +2,340 @@ import bpy import bpy_extras import sys from bpy.props import ( - BoolProperty, - IntProperty, - PointerProperty, - StringProperty, - EnumProperty, - ) + BoolProperty, + IntProperty, + PointerProperty, + StringProperty, + EnumProperty, + ) from mathutils import ( - Vector, - ) + Vector, + ) from bpy_extras.view3d_utils import ( - region_2d_to_vector_3d, - region_2d_to_origin_3d, - region_2d_to_location_3d, - location_3d_to_region_2d, + region_2d_to_vector_3d, + region_2d_to_origin_3d, + region_2d_to_location_3d, + location_3d_to_region_2d, ) from .carver_profils import ( - Profils - ) + Profils + ) from .carver_utils import ( - duplicateObject, - UndoListUpdate, - createMeshFromData, - SelectObject, - Selection_Save_Restore, - Selection_Save, - Selection_Restore, - update_grid, - objDiagonal, - Undo, - UndoAdd, - Pick, - rot_axis_quat, - MoveCursor, - Picking, - CreateCutSquare, - CreateCutCircle, - CreateCutLine, - boolean_operation, - update_bevel, - CreateBevel, - Rebool, - Snap_Cursor, - ) + duplicateObject, + UndoListUpdate, + createMeshFromData, + SelectObject, + Selection_Save_Restore, + Selection_Save, + Selection_Restore, + update_grid, + objDiagonal, + Undo, + UndoAdd, + Pick, + rot_axis_quat, + MoveCursor, + Picking, + CreateCutSquare, + CreateCutCircle, + CreateCutLine, + boolean_operation, + update_bevel, + CreateBevel, + Rebool, + Snap_Cursor, + ) from .carver_draw import draw_callback_px # Modal Operator class CARVER_OT_operator(bpy.types.Operator): - bl_idname = "carver.operator" - bl_label = "Carver" - bl_description = "Cut or create Meshes in Object mode" - bl_options = {'REGISTER', 'UNDO'} + bl_idname = "carver.operator" + bl_label = "Carver" + bl_description = "Cut or create Meshes in Object mode" + bl_options = {'REGISTER', 'UNDO'} - def __init__(self): - context = bpy.context - # Carve mode: Cut, Object, Profile - self.CutMode = False - self.CreateMode = False - self.ObjectMode = False - self.ProfileMode = False + def __init__(self): + context = bpy.context + # Carve mode: Cut, Object, Profile + self.CutMode = False + self.CreateMode = False + self.ObjectMode = False + self.ProfileMode = False - # Create mode - self.ExclusiveCreateMode = False - if len(context.selected_objects) == 0: - self.ExclusiveCreateMode = True - self.CreateMode = True + # Create mode + self.ExclusiveCreateMode = False + if len(context.selected_objects) == 0: + self.ExclusiveCreateMode = True + self.CreateMode = True - # Cut type (Rectangle, Circle, Line) - self.rectangle = 0 - self.line = 1 - self.circle = 2 + # Cut type (Rectangle, Circle, Line) + self.rectangle = 0 + self.line = 1 + self.circle = 2 - # Cut Rectangle coordinates - self.rectangle_coord = [] + # Cut Rectangle coordinates + self.rectangle_coord = [] - # Selected type of cut - self.CutType = 0 + # Selected type of cut + self.CutType = 0 - # Boolean operation - self.difference = 0 - self.union = 1 - - self.BoolOps = self.difference - - self.CurrentSelection = context.selected_objects.copy() - self.CurrentActive = context.active_object - self.all_sel_obj_list = context.selected_objects.copy() - self.save_active_obj = None - - args = (self, context) - self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL') - - self.mouse_path = [(0, 0), (0, 0)] - - # Keyboard event - self.shift = False - self.ctrl = False - self.alt = False - - self.dont_apply_boolean = context.scene.mesh_carver.DontApply - self.Auto_BevelUpdate = True - - # Circle variables - self.stepAngle = [2, 4, 5, 6, 9, 10, 15, 20, 30, 40, 45, 60, 72, 90] - self.step = 4 - - # Primitives Position - self.xpos = 0 - self.ypos = 0 - self.InitPosition = False - - # Close polygonal shape - self.Closed = False - - # Depth Cursor - self.snapCursor = context.scene.mesh_carver.DepthCursor - - # Help - self.AskHelp = False - - # Working object - self.OpsObj = context.active_object - - # Rebool forced (cut line) - self.ForceRebool = False - - self.ViewVector = Vector() - self.CurrentObj = None - - # Brush - self.BrushSolidify = False - self.WidthSolidify = False - self.CarveDepth = False - self.BrushDepth = False - self.BrushDepthOffset = 0.0 - self.snap = False - - self.ObjectScale = False - - #Init create circle primitive - self.CLR_C = [] - - # Cursor location - self.CurLoc = Vector((0.0, 0.0, 0.0)) - self.SavCurLoc = Vector((0.0, 0.0, 0.0)) - - # Mouse region - self.mouse_region = -1, -1 - self.SavMousePos = None - self.xSavMouse = 0 - - # Scale, rotate object - self.ascale = 0 - self.aRotZ = 0 - self.nRotZ = 0 - self.quat_rot_axis = None - self.quat_rot = None - - self.RandomRotation = context.scene.mesh_carver.ORandom - - self.ShowCursor = True - - self.Instantiate = context.scene.mesh_carver.OInstanciate - - self.ProfileBrush = None - self.ObjectBrush = None - - self.InitBrush = { - 'location' : None, - 'scale' : None, - 'rotation_quaternion' : None, - 'rotation_euler' : None, - 'display_type' : 'WIRE', - 'show_in_front' : False - } - - # Array variables - self.nbcol = 1 - self.nbrow = 1 - self.gapx = 0 - self.gapy = 0 - self.scale_x = 1 - self.scale_y = 1 - self.GridScaleX = False - self.GridScaleY = False - - @classmethod - def poll(cls, context): - ob = None - if len(context.selected_objects) > 0: - ob = context.selected_objects[0] - # Test if selected object or none (for create mode) - return ( - (ob and ob.type == 'MESH' and context.mode == 'OBJECT') or - (context.mode == 'OBJECT' and ob is None) or - (context.mode == 'EDIT_MESH')) - - def modal(self, context, event): - PI = 3.14156 - region_types = {'WINDOW', 'UI'} - win = context.window - - # Find the limit of the view3d region - self.check_region(context,event) - - for area in win.screen.areas: - if area.type == 'VIEW_3D': - for region in area.regions: - if not region_types or region.type in region_types: - region.tag_redraw() - - # Change the snap increment value using the wheel mouse - if self.CutMode: - if self.alt is False: - if self.ctrl and (self.CutType in (self.line, self.rectangle)): - # Get the VIEW3D area - for i, a in enumerate(context.screen.areas): - if a.type == 'VIEW_3D': - space = context.screen.areas[i].spaces.active - grid_scale = space.overlay.grid_scale - grid_subdivisions = space.overlay.grid_subdivisions - - if event.type == 'WHEELUPMOUSE': - space.overlay.grid_subdivisions += 1 - elif event.type == 'WHEELDOWNMOUSE': - space.overlay.grid_subdivisions -= 1 - - if event.type in { - 'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE', - 'NUMPAD_1', 'NUMPAD_2', 'NUMPAD_3', 'NUMPAD_4', 'NUMPAD_6', - 'NUMPAD_7', 'NUMPAD_8', 'NUMPAD_9', 'NUMPAD_5'}: - return {'PASS_THROUGH'} - - try: - # [Shift] - self.shift = True if event.shift else False - - # [Ctrl] - self.ctrl = True if event.ctrl else False - - # [Alt] - self.alt = False - - # [Alt] press : Init position variable before moving the cut brush with LMB - if event.alt: - if self.InitPosition is False: - self.xpos = 0 - self.ypos = 0 - self.last_mouse_region_x = event.mouse_region_x - self.last_mouse_region_y = event.mouse_region_y - self.InitPosition = True - self.alt = True - - # [Alt] release : update the coordinates - if self.InitPosition and self.alt is False: - for i in range(0, len(self.mouse_path)): - l = list(self.mouse_path[i]) - l[0] += self.xpos - l[1] += self.ypos - self.mouse_path[i] = tuple(l) - - self.xpos = self.ypos = 0 - self.InitPosition = False - - if event.type == 'SPACE' and event.value == 'PRESS': - # If object or profile mode is TRUE : Confirm the cut - if self.ObjectMode or self.ProfileMode: - # If array, remove double with intersect meshes - if ((self.nbcol + self.nbrow) > 3): - # Go in edit mode mode - bpy.ops.object.mode_set(mode='EDIT') - # Remove duplicate vertices - bpy.ops.mesh.remove_doubles() - # Return in object mode - bpy.ops.object.mode_set(mode='OBJECT') - - if self.alt: - # Save selected objects - self.all_sel_obj_list = context.selected_objects.copy() - if len(context.selected_objects) > 0: - bpy.ops.object.select_all(action='TOGGLE') - - if self.ObjectMode: - SelectObject(self, self.ObjectBrush) - else: - SelectObject(self, self.ProfileBrush) - duplicateObject(self) - else: - # Brush Cut - self.Cut() - # Save selected objects - if self.ObjectMode: - if len(self.ObjectBrush.children) > 0: - self.all_sel_obj_list = context.selected_objects.copy() - if len(context.selected_objects) > 0: - bpy.ops.object.select_all(action='TOGGLE') - - if self.ObjectMode: - SelectObject(self, self.ObjectBrush) - else: - SelectObject(self, self.ProfileBrush) - duplicateObject(self) - - UndoListUpdate(self) - - # Save cursor position - self.SavMousePos = self.CurLoc - else: - if self.CutMode is False: - # Cut Mode - self.CutType += 1 - if self.CutType > 2: - self.CutType = 0 - else: - if self.CutType == self.line: - # Cuts creation - CreateCutLine(self, context) - if self.CreateMode: - # Object creation - self.CreateGeometry() - bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') - # Cursor Snap - context.scene.mesh_carver.DepthCursor = self.snapCursor - # Object Instantiate - context.scene.mesh_carver.OInstanciate = self.Instantiate - # Random rotation - context.scene.mesh_carver.ORandom = self.RandomRotation - - return {'FINISHED'} - else: - self.Cut() - UndoListUpdate(self) + # Boolean operation + self.difference = 0 + self.union = 1 + + self.BoolOps = self.difference + + self.CurrentSelection = context.selected_objects.copy() + self.CurrentActive = context.active_object + self.all_sel_obj_list = context.selected_objects.copy() + self.save_active_obj = None + + args = (self, context) + self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL') + + self.mouse_path = [(0, 0), (0, 0)] + + # Keyboard event + self.shift = False + self.ctrl = False + self.alt = False + + self.dont_apply_boolean = context.scene.mesh_carver.DontApply + self.Auto_BevelUpdate = True + + # Circle variables + self.stepAngle = [2, 4, 5, 6, 9, 10, 15, 20, 30, 40, 45, 60, 72, 90] + self.step = 4 + + # Primitives Position + self.xpos = 0 + self.ypos = 0 + self.InitPosition = False + + # Close polygonal shape + self.Closed = False + + # Depth Cursor + self.snapCursor = context.scene.mesh_carver.DepthCursor + + # Help + self.AskHelp = False + + # Working object + self.OpsObj = context.active_object + + # Rebool forced (cut line) + self.ForceRebool = False + + self.ViewVector = Vector() + self.CurrentObj = None + + # Brush + self.BrushSolidify = False + self.WidthSolidify = False + self.CarveDepth = False + self.BrushDepth = False + self.BrushDepthOffset = 0.0 + self.snap = False + + self.ObjectScale = False + + #Init create circle primitive + self.CLR_C = [] + + # Cursor location + self.CurLoc = Vector((0.0, 0.0, 0.0)) + self.SavCurLoc = Vector((0.0, 0.0, 0.0)) + + # Mouse region + self.mouse_region = -1, -1 + self.SavMousePos = None + self.xSavMouse = 0 + + # Scale, rotate object + self.ascale = 0 + self.aRotZ = 0 + self.nRotZ = 0 + self.quat_rot_axis = None + self.quat_rot = None + + self.RandomRotation = context.scene.mesh_carver.ORandom + + self.ShowCursor = True + + self.Instantiate = context.scene.mesh_carver.OInstanciate + + self.ProfileBrush = None + self.ObjectBrush = None + + self.InitBrush = { + 'location' : None, + 'scale' : None, + 'rotation_quaternion' : None, + 'rotation_euler' : None, + 'display_type' : 'WIRE', + 'show_in_front' : False + } + + # Array variables + self.nbcol = 1 + self.nbrow = 1 + self.gapx = 0 + self.gapy = 0 + self.scale_x = 1 + self.scale_y = 1 + self.GridScaleX = False + self.GridScaleY = False + + @classmethod + def poll(cls, context): + ob = None + if len(context.selected_objects) > 0: + ob = context.selected_objects[0] + # Test if selected object or none (for create mode) + return ( + (ob and ob.type == 'MESH' and context.mode == 'OBJECT') or + (context.mode == 'OBJECT' and ob is None) or + (context.mode == 'EDIT_MESH')) + + def modal(self, context, event): + PI = 3.14156 + region_types = {'WINDOW', 'UI'} + win = context.window + + # Find the limit of the view3d region + self.check_region(context,event) + + for area in win.screen.areas: + if area.type == 'VIEW_3D': + for region in area.regions: + if not region_types or region.type in region_types: + region.tag_redraw() + + # Change the snap increment value using the wheel mouse + if self.CutMode: + if self.alt is False: + if self.ctrl and (self.CutType in (self.line, self.rectangle)): + # Get the VIEW3D area + for i, a in enumerate(context.screen.areas): + if a.type == 'VIEW_3D': + space = context.screen.areas[i].spaces.active + grid_scale = space.overlay.grid_scale + grid_subdivisions = space.overlay.grid_subdivisions + + if event.type == 'WHEELUPMOUSE': + space.overlay.grid_subdivisions += 1 + elif event.type == 'WHEELDOWNMOUSE': + space.overlay.grid_subdivisions -= 1 + + if event.type in { + 'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE', + 'NUMPAD_1', 'NUMPAD_2', 'NUMPAD_3', 'NUMPAD_4', 'NUMPAD_6', + 'NUMPAD_7', 'NUMPAD_8', 'NUMPAD_9', 'NUMPAD_5'}: + return {'PASS_THROUGH'} + + try: + # [Shift] + self.shift = True if event.shift else False + + # [Ctrl] + self.ctrl = True if event.ctrl else False + + # [Alt] + self.alt = False + + # [Alt] press : Init position variable before moving the cut brush with LMB + if event.alt: + if self.InitPosition is False: + self.xpos = 0 + self.ypos = 0 + self.last_mouse_region_x = event.mouse_region_x + self.last_mouse_region_y = event.mouse_region_y + self.InitPosition = True + self.alt = True + + # [Alt] release : update the coordinates + if self.InitPosition and self.alt is False: + for i in range(0, len(self.mouse_path)): + l = list(self.mouse_path[i]) + l[0] += self.xpos + l[1] += self.ypos + self.mouse_path[i] = tuple(l) + + self.xpos = self.ypos = 0 + self.InitPosition = False + + if event.type == 'SPACE' and event.value == 'PRESS': + # If object or profile mode is TRUE : Confirm the cut + if self.ObjectMode or self.ProfileMode: + # If array, remove double with intersect meshes + if ((self.nbcol + self.nbrow) > 3): + # Go in edit mode mode + bpy.ops.object.mode_set(mode='EDIT') + # Remove duplicate vertices + bpy.ops.mesh.remove_doubles() + # Return in object mode + bpy.ops.object.mode_set(mode='OBJECT') + + if self.alt: + # Save selected objects + self.all_sel_obj_list = context.selected_objects.copy() + if len(context.selected_objects) > 0: + bpy.ops.object.select_all(action='TOGGLE') + + if self.ObjectMode: + SelectObject(self, self.ObjectBrush) + else: + SelectObject(self, self.ProfileBrush) + duplicateObject(self) + else: + # Brush Cut + self.Cut() + # Save selected objects + if self.ObjectMode: + if len(self.ObjectBrush.children) > 0: + self.all_sel_obj_list = context.selected_objects.copy() + if len(context.selected_objects) > 0: + bpy.ops.object.select_all(action='TOGGLE') + + if self.ObjectMode: + SelectObject(self, self.ObjectBrush) + else: + SelectObject(self, self.ProfileBrush) + duplicateObject(self) + + UndoListUpdate(self) + + # Save cursor position + self.SavMousePos = self.CurLoc + else: + if self.CutMode is False: + # Cut Mode + self.CutType += 1 + if self.CutType > 2: + self.CutType = 0 + else: + if self.CutType == self.line: + # Cuts creation + CreateCutLine(self, context) + if self.CreateMode: + # Object creation + self.CreateGeometry() + bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') + # Cursor Snap + context.scene.mesh_carver.DepthCursor = self.snapCursor + # Object Instantiate + context.scene.mesh_carver.OInstanciate = self.Instantiate + # Random rotation + context.scene.mesh_carver.ORandom = self.RandomRotation + + return {'FINISHED'} + else: + self.Cut() + UndoListUpdate(self) #----------------------------------------------------- @@ -343,1005 +343,1005 @@ class CARVER_OT_operator(bpy.types.Operator): #----------------------------------------------------- - # Object creation - if event.type == self.carver_prefs.Key_Create and event.value == 'PRESS': - if self.ExclusiveCreateMode is False: - self.CreateMode = not self.CreateMode - - # Auto Bevel Update - if event.type == self.carver_prefs.Key_Update and event.value == 'PRESS': - self.Auto_BevelUpdate = not self.Auto_BevelUpdate - - # Boolean operation type - if event.type == self.carver_prefs.Key_Bool and event.value == 'PRESS': - if (self.ProfileMode is True) or (self.ObjectMode is True): - if self.BoolOps == self.difference: - self.BoolOps = self.union - else: - self.BoolOps = self.difference - - # Brush Mode - if event.type == self.carver_prefs.Key_Brush and event.value == 'PRESS': - self.dont_apply_boolean = False - if (self.ProfileMode is False) and (self.ObjectMode is False): - self.ProfileMode = True - else: - self.ProfileMode = False - if self.ObjectBrush is not None: - if self.ObjectMode is False: - self.ObjectMode = True - self.BrushSolidify = False - self.CList = self.OB_List - - Selection_Save_Restore(self) - context.scene.mesh_carver.nProfile = self.nProfil - else: - self.ObjectMode = False - else: - self.BrushSolidify = False - Selection_Save_Restore(self) - - if self.ProfileMode: - createMeshFromData(self) - self.ProfileBrush = bpy.data.objects["CT_Profil"] - Selection_Save(self) - self.BrushSolidify = True - - bpy.ops.object.select_all(action='TOGGLE') - self.ProfileBrush.select_set(True) - context.view_layer.objects.active = self.ProfileBrush - # Set xRay - self.ProfileBrush.show_in_front = True - - bpy.ops.object.modifier_add(type='SOLIDIFY') - context.object.modifiers["Solidify"].name = "CT_SOLIDIFY" - context.object.modifiers["CT_SOLIDIFY"].thickness = 0.1 - - Selection_Restore(self) - - self.CList = self.CurrentSelection - else: - if self.ObjectBrush is not None: - if self.ObjectMode is False: - if self.ObjectBrush is not None: - self.ObjectBrush.location = self.InitBrush['location'] - self.ObjectBrush.scale = self.InitBrush['scale'] - self.ObjectBrush.rotation_quaternion = self.InitBrush['rotation_quaternion'] - self.ObjectBrush.rotation_euler = self.InitBrush['rotation_euler'] - self.ObjectBrush.display_type = self.InitBrush['display_type'] - self.ObjectBrush.show_in_front = self.InitBrush['show_in_front'] - - #Store active and selected objects - Selection_Save(self) - - #Remove Carver modifier - self.BrushSolidify = False - bpy.ops.object.select_all(action='TOGGLE') - self.ObjectBrush.select_set(True) - context.view_layer.objects.active = self.ObjectBrush - bpy.ops.object.modifier_remove(modifier="CT_SOLIDIFY") - - #Restore selected and active object - Selection_Restore(self) - else: - if self.SolidifyPossible: - #Store active and selected objects - Selection_Save(self) - self.BrushSolidify = True - bpy.ops.object.select_all(action='TOGGLE') - self.ObjectBrush.select_set(True) - context.view_layer.objects.active = self.ObjectBrush - # Set xRay - self.ObjectBrush.show_in_front = True - bpy.ops.object.modifier_add(type='SOLIDIFY') - context.object.modifiers["Solidify"].name = "CT_SOLIDIFY" - context.object.modifiers["CT_SOLIDIFY"].thickness = 0.1 - - #Restore selected and active object - Selection_Restore(self) - - # Help display - if event.type == self.carver_prefs.Key_Help and event.value == 'PRESS': - self.AskHelp = not self.AskHelp - - # Instantiate object - if event.type == self.carver_prefs.Key_Instant and event.value == 'PRESS': - self.Instantiate = not self.Instantiate - - # Close polygonal shape - if event.type == self.carver_prefs.Key_Close and event.value == 'PRESS': - if self.CreateMode: - self.Closed = not self.Closed - - if event.type == self.carver_prefs.Key_Apply and event.value == 'PRESS': - self.dont_apply_boolean = not self.dont_apply_boolean - - # Scale object - if event.type == self.carver_prefs.Key_Scale and event.value == 'PRESS': - if self.ObjectScale is False: - self.mouse_region = event.mouse_region_x, event.mouse_region_y - self.ObjectScale = True - - # Grid : Snap on grid - if event.type == self.carver_prefs.Key_Snap and event.value == 'PRESS': - self.snap = not self.snap - - # Array : Add column - if event.type == 'UP_ARROW' and event.value == 'PRESS': - self.nbrow += 1 - update_grid(self, context) - - # Array : Delete column - elif event.type == 'DOWN_ARROW' and event.value == 'PRESS': - self.nbrow -= 1 - update_grid(self, context) - - # Array : Add row - elif event.type == 'RIGHT_ARROW' and event.value == 'PRESS': - self.nbcol += 1 - update_grid(self, context) - - # Array : Delete row - elif event.type == 'LEFT_ARROW' and event.value == 'PRESS': - self.nbcol -= 1 - update_grid(self, context) - - # Array : Scale gap between columns - if event.type == self.carver_prefs.Key_Gapy and event.value == 'PRESS': - if self.GridScaleX is False: - self.mouse_region = event.mouse_region_x, event.mouse_region_y - self.GridScaleX = True - - # Array : Scale gap between rows - if event.type == self.carver_prefs.Key_Gapx and event.value == 'PRESS': - if self.GridScaleY is False: - self.mouse_region = event.mouse_region_x, event.mouse_region_y - self.GridScaleY = True - - # Cursor depth or solidify pattern - if event.type == self.carver_prefs.Key_Depth and event.value == 'PRESS': - if (self.ObjectMode is False) and (self.ProfileMode is False): - self.snapCursor = not self.snapCursor - else: - # Solidify - - if (self.ObjectMode or self.ProfileMode) and (self.SolidifyPossible): - solidify = True - - if self.ObjectMode: - z = self.ObjectBrush.data.vertices[0].co.z - ErrorMarge = 0.01 - for v in self.ObjectBrush.data.vertices: - if abs(v.co.z - z) > ErrorMarge: - solidify = False - self.CarveDepth = True - self.mouse_region = event.mouse_region_x, event.mouse_region_y - break - - if solidify: - if self.ObjectMode: - for mb in self.ObjectBrush.modifiers: - if mb.type == 'SOLIDIFY': - AlreadySoldify = True - else: - for mb in self.ProfileBrush.modifiers: - if mb.type == 'SOLIDIFY': - AlreadySoldify = True - - if AlreadySoldify is False: - Selection_Save(self) - self.BrushSolidify = True - - bpy.ops.object.select_all(action='TOGGLE') - if self.ObjectMode: - self.ObjectBrush.select_set(True) - context.view_layer.objects.active = self.ObjectBrush - # Active le xray - self.ObjectBrush.show_in_front = True - else: - self.ProfileBrush.select_set(True) - context.view_layer.objects.active = self.ProfileBrush - # Active le xray - self.ProfileBrush.show_in_front = True - - bpy.ops.object.modifier_add(type='SOLIDIFY') - context.object.modifiers["Solidify"].name = "CT_SOLIDIFY" - - context.object.modifiers["CT_SOLIDIFY"].thickness = 0.1 - - Selection_Restore(self) - - self.WidthSolidify = not self.WidthSolidify - self.mouse_region = event.mouse_region_x, event.mouse_region_y - - if event.type == self.carver_prefs.Key_BrushDepth and event.value == 'PRESS': - if self.ObjectMode: - self.CarveDepth = False - - self.BrushDepth = True - self.mouse_region = event.mouse_region_x, event.mouse_region_y - - # Random rotation - if event.type == 'R' and event.value == 'PRESS': - self.RandomRotation = not self.RandomRotation - - # Undo - if event.type == 'Z' and event.value == 'PRESS': - if self.ctrl: - if (self.CutType == self.line) and (self.CutMode): - if len(self.mouse_path) > 1: - self.mouse_path[len(self.mouse_path) - 1:] = [] - else: - Undo(self) - - # Mouse move - if event.type == 'MOUSEMOVE' : - if self.ObjectMode or self.ProfileMode: - fac = 50.0 - if self.shift: - fac = 500.0 - if self.WidthSolidify: - if self.ObjectMode: - bpy.data.objects[self.ObjectBrush.name].modifiers[ - "CT_SOLIDIFY"].thickness += (event.mouse_region_x - self.mouse_region[0]) / fac - elif self.ProfileMode: - bpy.data.objects[self.ProfileBrush.name].modifiers[ - "CT_SOLIDIFY"].thickness += (event.mouse_region_x - self.mouse_region[0]) / fac - self.mouse_region = event.mouse_region_x, event.mouse_region_y - elif self.CarveDepth: - for v in self.ObjectBrush.data.vertices: - v.co.z += (event.mouse_region_x - self.mouse_region[0]) / fac - self.mouse_region = event.mouse_region_x, event.mouse_region_y - elif self.BrushDepth: - self.BrushDepthOffset += (event.mouse_region_x - self.mouse_region[0]) / fac - self.mouse_region = event.mouse_region_x, event.mouse_region_y - else: - if (self.GridScaleX): - self.gapx += (event.mouse_region_x - self.mouse_region[0]) / 50 - self.mouse_region = event.mouse_region_x, event.mouse_region_y - update_grid(self, context) - return {'RUNNING_MODAL'} - - elif (self.GridScaleY): - self.gapy += (event.mouse_region_x - self.mouse_region[0]) / 50 - self.mouse_region = event.mouse_region_x, event.mouse_region_y - update_grid(self, context) - return {'RUNNING_MODAL'} - - elif self.ObjectScale: - self.ascale = -(event.mouse_region_x - self.mouse_region[0]) - self.mouse_region = event.mouse_region_x, event.mouse_region_y - - if self.ObjectMode: - self.ObjectBrush.scale.x -= float(self.ascale) / 150.0 - if self.ObjectBrush.scale.x <= 0.0: - self.ObjectBrush.scale.x = 0.0 - self.ObjectBrush.scale.y -= float(self.ascale) / 150.0 - if self.ObjectBrush.scale.y <= 0.0: - self.ObjectBrush.scale.y = 0.0 - self.ObjectBrush.scale.z -= float(self.ascale) / 150.0 - if self.ObjectBrush.scale.z <= 0.0: - self.ObjectBrush.scale.z = 0.0 - - elif self.ProfileMode: - if self.ProfileBrush is not None: - self.ProfileBrush.scale.x -= float(self.ascale) / 150.0 - self.ProfileBrush.scale.y -= float(self.ascale) / 150.0 - self.ProfileBrush.scale.z -= float(self.ascale) / 150.0 - else: - if self.LMB: - if self.ctrl: - self.aRotZ = - \ - ((int((event.mouse_region_x - self.xSavMouse) / 10.0) * PI / 4.0) * 25.0) - else: - self.aRotZ -= event.mouse_region_x - self.mouse_region[0] - self.ascale = 0.0 - - self.mouse_region = event.mouse_region_x, event.mouse_region_y - else: - target_hit, target_normal, target_eul_rotation = Pick(context, event, self) - if target_hit is not None: - self.ShowCursor = True - up_vector = Vector((0.0, 0.0, 1.0)) - quat_rot_axis = rot_axis_quat(up_vector, target_normal) - self.quat_rot = target_eul_rotation @ quat_rot_axis - MoveCursor(quat_rot_axis, target_hit, self) - self.SavCurLoc = target_hit - if self.ctrl: - if self.SavMousePos is not None: - xEcart = abs(self.SavMousePos.x - self.SavCurLoc.x) - yEcart = abs(self.SavMousePos.y - self.SavCurLoc.y) - zEcart = abs(self.SavMousePos.z - self.SavCurLoc.z) - if (xEcart > yEcart) and (xEcart > zEcart): - self.CurLoc = Vector( - (target_hit.x, self.SavMousePos.y, self.SavMousePos.z)) - if (yEcart > xEcart) and (yEcart > zEcart): - self.CurLoc = Vector( - (self.SavMousePos.x, target_hit.y, self.SavMousePos.z)) - if (zEcart > xEcart) and (zEcart > yEcart): - self.CurLoc = Vector( - (self.SavMousePos.x, self.SavMousePos.y, target_hit.z)) - else: - self.CurLoc = target_hit - else: - self.CurLoc = target_hit - else: - if self.CutMode: - if self.alt is False: - if self.ctrl : - # Find the closest position on the overlay grid and snap the mouse on it - # Draw a mini grid around the cursor - mouse_pos = [[event.mouse_region_x, event.mouse_region_y]] - Snap_Cursor(self, context, event, mouse_pos) - - else: - if len(self.mouse_path) > 0: - self.mouse_path[len(self.mouse_path) - - 1] = (event.mouse_region_x, event.mouse_region_y) - else: - # [ALT] press, update position - self.xpos += (event.mouse_region_x - self.last_mouse_region_x) - self.ypos += (event.mouse_region_y - self.last_mouse_region_y) - - self.last_mouse_region_x = event.mouse_region_x - self.last_mouse_region_y = event.mouse_region_y - - elif event.type == 'LEFTMOUSE' and event.value == 'PRESS': - if self.ObjectMode or self.ProfileMode: - if self.LMB is False: - target_hit, target_normal, target_eul_rotation = Pick(context, event, self) - if target_hit is not None: - up_vector = Vector((0.0, 0.0, 1.0)) - self.quat_rot_axis = rot_axis_quat(up_vector, target_normal) - self.quat_rot = target_eul_rotation @ self.quat_rot_axis - self.mouse_region = event.mouse_region_x, event.mouse_region_y - self.xSavMouse = event.mouse_region_x - - if self.ctrl: - self.nRotZ = int((self.aRotZ / 25.0) / (PI / 4.0)) - self.aRotZ = self.nRotZ * (PI / 4.0) * 25.0 - - self.LMB = True - - # LEFTMOUSE - elif event.type == 'LEFTMOUSE' and event.value == 'RELEASE' and self.in_view_3d: - if self.ObjectMode or self.ProfileMode: - # Rotation and scale - self.LMB = False - if self.ObjectScale is True: - self.ObjectScale = False - - if self.GridScaleX is True: - self.GridScaleX = False - - if self.GridScaleY is True: - self.GridScaleY = False - - if self.WidthSolidify: - self.WidthSolidify = False - - if self.CarveDepth is True: - self.CarveDepth = False - - if self.BrushDepth is True: - self.BrushDepth = False - - else: - if self.CutMode is False: - if self.ctrl: - Picking(context, event) - - else: - - if self.CutType == self.line: - if self.CutMode is False: - self.mouse_path.clear() - self.mouse_path.append((event.mouse_region_x, event.mouse_region_y)) - self.mouse_path.append((event.mouse_region_x, event.mouse_region_y)) - else: - self.mouse_path[0] = (event.mouse_region_x, event.mouse_region_y) - self.mouse_path[1] = (event.mouse_region_x, event.mouse_region_y) - self.CutMode = True - else: - if self.CutType != self.line: - # Cut creation - if self.CutType == self.rectangle: - CreateCutSquare(self, context) - if self.CutType == self.circle: - CreateCutCircle(self, context) - if self.CutType == self.line: - CreateCutLine(self, context) - - if self.CreateMode: - # Object creation - self.CreateGeometry() - bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') - # Depth Cursor - context.scene.mesh_carver.DepthCursor = self.snapCursor - # Instantiate object - context.scene.mesh_carver.OInstanciate = self.Instantiate - # Random rotation - context.scene.mesh_carver.ORandom = self.RandomRotation - # Apply operation - context.scene.mesh_carver.DontApply = self.dont_apply_boolean - - # if Object mode, set initiale state - if self.ObjectBrush is not None: - self.ObjectBrush.location = self.InitBrush['location'] - self.ObjectBrush.scale = self.InitBrush['scale'] - self.ObjectBrush.rotation_quaternion = self.InitBrush['rotation_quaternion'] - self.ObjectBrush.rotation_euler = self.InitBrush['rotation_euler'] - self.ObjectBrush.display_type = self.InitBrush['display_type'] - self.ObjectBrush.show_in_front = self.InitBrush['show_in_front'] - - # remove solidify - Selection_Save(self) - self.BrushSolidify = False - - bpy.ops.object.select_all(action='TOGGLE') - self.ObjectBrush.select_set(True) - context.view_layer.objects.active = self.ObjectBrush - - bpy.ops.object.modifier_remove(modifier="CT_SOLIDIFY") - - Selection_Restore(self) - - context.scene.mesh_carver.nProfile = self.nProfil - - return {'FINISHED'} - else: - self.Cut() - UndoListUpdate(self) - else: - # Line - self.mouse_path.append((event.mouse_region_x, event.mouse_region_y)) - - # Change brush profil or circle subdivisions - elif (event.type == 'COMMA' and event.value == 'PRESS') or \ - (event.type == self.carver_prefs.Key_Subrem and event.value == 'PRESS'): - # Brush profil - if self.ProfileMode: - self.nProfil += 1 - if self.nProfil >= self.MaxProfil: - self.nProfil = 0 - createMeshFromData(self) - # Circle subdivisions - if self.CutType == self.circle: - self.step += 1 - if self.step >= len(self.stepAngle): - self.step = len(self.stepAngle) - 1 - # Change brush profil or circle subdivisions - elif (event.type == 'PERIOD' and event.value == 'PRESS') or \ - (event.type == self.carver_prefs.Key_Subadd and event.value == 'PRESS'): - # Brush profil - if self.ProfileMode: - self.nProfil -= 1 - if self.nProfil < 0: - self.nProfil = self.MaxProfil - 1 - createMeshFromData(self) - # Circle subdivisions - if self.CutType == self.circle: - if self.step > 0: - self.step -= 1 - # Quit - elif event.type in {'RIGHTMOUSE', 'ESC'}: - # Depth Cursor - context.scene.mesh_carver.DepthCursor = self.snapCursor - # Instantiate object - context.scene.mesh_carver.OInstanciate = self.Instantiate - # Random Rotation - context.scene.mesh_carver.ORandom = self.RandomRotation - # Apply boolean operation - context.scene.mesh_carver.DontApply = self.dont_apply_boolean - - # Reset Object - if self.ObjectBrush is not None: - self.ObjectBrush.location = self.InitBrush['location'] - self.ObjectBrush.scale = self.InitBrush['scale'] - self.ObjectBrush.rotation_quaternion = self.InitBrush['rotation_quaternion'] - self.ObjectBrush.rotation_euler = self.InitBrush['rotation_euler'] - self.ObjectBrush.display_type = self.InitBrush['display_type'] - self.ObjectBrush.show_in_front = self.InitBrush['show_in_front'] - - # Remove solidify modifier - Selection_Save(self) - self.BrushSolidify = False - - bpy.ops.object.select_all(action='TOGGLE') - self.ObjectBrush.select_set(True) - context.view_layer.objects.active = self.ObjectBrush - - bpy.ops.object.modifier_remove(modifier="CT_SOLIDIFY") - bpy.ops.object.select_all(action='TOGGLE') - - Selection_Restore(self) - - Selection_Save_Restore(self) - context.view_layer.objects.active = self.CurrentActive - context.scene.mesh_carver.nProfile = self.nProfil - - bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') - - # Remove Copy Object Brush - if bpy.data.objects.get("CarverBrushCopy") is not None: - brush = bpy.data.objects["CarverBrushCopy"] - self.ObjectBrush.data = bpy.data.meshes[brush.data.name] - bpy.ops.object.select_all(action='DESELECT') - bpy.data.objects["CarverBrushCopy"].select_set(True) - bpy.ops.object.delete() - - return {'FINISHED'} - - return {'RUNNING_MODAL'} - - except: - print("\n[Carver MT ERROR]\n") - import traceback - traceback.print_exc() - - context.window.cursor_modal_set("DEFAULT") - context.area.header_text_set(None) - bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') - - self.report({'WARNING'}, - "Operation finished. Failure during Carving (Check the console for more info)") - - return {'FINISHED'} - - def cancel(self, context): - # Note: used to prevent memory leaks on quitting Blender while the modal operator - # is still running, gets called on return {"CANCELLED"} - bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') - - def invoke(self, context, event): - if context.area.type != 'VIEW_3D': - self.report({'WARNING'}, - "View3D not found or not currently active. Operation Cancelled") - self.cancel(context) - return {'CANCELLED'} - - # test if some other object types are selected that are not meshes - for obj in context.selected_objects: - if obj.type != "MESH": - self.report({'WARNING'}, - "Some selected objects are not of the Mesh type. Operation Cancelled") - self.cancel(context) - return {'CANCELLED'} - - if context.mode == 'EDIT_MESH': - bpy.ops.object.mode_set(mode='OBJECT') - - #Load the Carver preferences - self.carver_prefs = bpy.context.preferences.addons[__package__].preferences - - # Get default patterns - self.Profils = [] - for p in Profils: - self.Profils.append((p[0], p[1], p[2], p[3])) - - for o in context.scene.objects: - if not o.name.startswith(self.carver_prefs.ProfilePrefix): - continue - # In-scene profiles may have changed, remove them to refresh - for m in bpy.data.meshes: - if m.name.startswith(self.carver_prefs.ProfilePrefix): - bpy.data.meshes.remove(m) - - vertices = [] - for v in o.data.vertices: - vertices.append((v.co.x, v.co.y, v.co.z)) - - faces = [] - for f in o.data.polygons: - face = [] - for v in f.vertices: - face.append(v) - - faces.append(face) - - self.Profils.append( - (o.name, - Vector((o.location.x, o.location.y, o.location.z)), - vertices, faces) - ) - - self.nProfil = context.scene.mesh_carver.nProfile - self.MaxProfil = len(self.Profils) - - - # reset selected profile if last profile exceeds length of array - if self.nProfil >= self.MaxProfil: - self.nProfil = context.scene.mesh_carver.nProfile = 0 - - if len(context.selected_objects) > 1: - self.ObjectBrush = context.active_object - - # Copy the brush object - ob = bpy.data.objects.new("CarverBrushCopy", context.object.data.copy()) - ob.location = self.ObjectBrush.location - context.collection.objects.link(ob) - context.view_layer.update() - - # Save default variables - self.InitBrush['location'] = self.ObjectBrush.location.copy() - self.InitBrush['scale'] = self.ObjectBrush.scale.copy() - self.InitBrush['rotation_quaternion'] = self.ObjectBrush.rotation_quaternion.copy() - self.InitBrush['rotation_euler'] = self.ObjectBrush.rotation_euler.copy() - self.InitBrush['display_type'] = self.ObjectBrush.display_type - self.InitBrush['show_in_front'] = self.ObjectBrush.show_in_front - - # Test if flat object - z = self.ObjectBrush.data.vertices[0].co.z - ErrorMarge = 0.01 - self.SolidifyPossible = True - for v in self.ObjectBrush.data.vertices: - if abs(v.co.z - z) > ErrorMarge: - self.SolidifyPossible = False - break - - self.CList = [] - self.OPList = [] - self.RList = [] - self.OB_List = [] - - for obj in context.selected_objects: - if obj != self.ObjectBrush: - self.OB_List.append(obj) - - # Left button - self.LMB = False - - # Undo Variables - self.undo_index = 0 - self.undo_limit = context.preferences.edit.undo_steps - self.undo_list = [] - - # Boolean operations type - self.BooleanType = 0 - - self.UList = [] - self.UList_Index = -1 - self.UndoOps = [] - - context.window_manager.modal_handler_add(self) - return {'RUNNING_MODAL'} - - #Get the region area where the operator is used - def check_region(self,context,event): - if context.area != None: - if context.area.type == "VIEW_3D" : - for region in context.area.regions: - if region.type == "TOOLS": - t_panel = region - elif region.type == "UI": - ui_panel = region - - view_3d_region_x = Vector((context.area.x + t_panel.width, context.area.x + context.area.width - ui_panel.width)) - view_3d_region_y = Vector((context.region.y, context.region.y+context.region.height)) - - if (event.mouse_x > view_3d_region_x[0] and event.mouse_x < view_3d_region_x[1] \ - and event.mouse_y > view_3d_region_y[0] and event.mouse_y < view_3d_region_y[1]): - self.in_view_3d = True - else: - self.in_view_3d = False - else: - self.in_view_3d = False - - def CreateGeometry(self): - context = bpy.context - in_local_view = False - - for area in context.screen.areas: - if area.type == 'VIEW_3D': - if area.spaces[0].local_view is not None: - in_local_view = True - - if in_local_view: - bpy.ops.view3d.localview() - - if self.ExclusiveCreateMode: - # Default width - objBBDiagonal = 0.5 - else: - ActiveObj = self.CurrentSelection[0] - if ActiveObj is not None: - # Object dimensions - objBBDiagonal = objDiagonal(ActiveObj) / 4 - subdivisions = 2 - - if len(context.selected_objects) > 0: - bpy.ops.object.select_all(action='TOGGLE') - - context.view_layer.objects.active = self.CurrentObj - - bpy.data.objects[self.CurrentObj.name].select_set(True) - bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY') - - bpy.ops.object.mode_set(mode='EDIT') - bpy.ops.mesh.select_all(action='SELECT') - bpy.ops.mesh.select_mode(type="EDGE") - if self.snapCursor is False: - bpy.ops.transform.translate(value=self.ViewVector * objBBDiagonal * subdivisions) - bpy.ops.mesh.extrude_region_move( - TRANSFORM_OT_translate={"value": -self.ViewVector * objBBDiagonal * subdivisions * 2}) - - bpy.ops.mesh.select_all(action='SELECT') - bpy.ops.mesh.normals_make_consistent() - bpy.ops.object.mode_set(mode='OBJECT') - - saved_location_0 = context.scene.cursor.location.copy() - bpy.ops.view3d.snap_cursor_to_active() - saved_location = context.scene.cursor.location.copy() - bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) - context.scene.cursor.location = saved_location - bpy.ops.object.origin_set(type='ORIGIN_CURSOR') - context.scene.cursor.location = saved_location_0 - - bpy.data.objects[self.CurrentObj.name].select_set(True) - bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY') - - for o in self.all_sel_obj_list: - bpy.data.objects[o.name].select_set(True) - - if in_local_view: - bpy.ops.view3d.localview() - - self.CutMode = False - self.mouse_path.clear() - self.mouse_path = [(0, 0), (0, 0)] - - def Cut(self): - context = bpy.context - - # Local view ? - in_local_view = False - for area in context.screen.areas: - if area.type == 'VIEW_3D': - if area.spaces[0].local_view is not None: - in_local_view = True - - if in_local_view: - bpy.ops.view3d.localview() - - # Save cursor position - CursorLocation = context.scene.cursor.location.copy() - - #List of selected objects - selected_obj_list = [] - - #Cut Mode with line - if (self.ObjectMode is False) and (self.ProfileMode is False): - - #Compute the bounding Box - objBBDiagonal = objDiagonal(self.CurrentSelection[0]) - if self.dont_apply_boolean: - subdivisions = 1 - else: - subdivisions = 32 - - # Get selected objects - selected_obj_list = context.selected_objects.copy() - - bpy.ops.object.select_all(action='TOGGLE') - - context.view_layer.objects.active = self.CurrentObj - - bpy.data.objects[self.CurrentObj.name].select_set(True) - bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY') - - bpy.ops.object.mode_set(mode='EDIT') - bpy.ops.mesh.select_all(action='SELECT') - bpy.ops.mesh.select_mode(type="EDGE") - #Translate the created mesh away from the view - if (self.snapCursor is False) or (self.ForceRebool): - bpy.ops.transform.translate(value=self.ViewVector * objBBDiagonal * subdivisions) - #Extrude the mesh region and move the result - bpy.ops.mesh.extrude_region_move( - TRANSFORM_OT_translate={"value": -self.ViewVector * objBBDiagonal * subdivisions * 2}) - bpy.ops.mesh.select_all(action='SELECT') - bpy.ops.mesh.normals_make_consistent() - bpy.ops.object.mode_set(mode='OBJECT') - else: - # Create list - if self.ObjectMode: - for o in self.CurrentSelection: - if o != self.ObjectBrush: - selected_obj_list.append(o) - self.CurrentObj = self.ObjectBrush - else: - selected_obj_list = self.CurrentSelection - self.CurrentObj = self.ProfileBrush - - for obj in self.CurrentSelection: - UndoAdd(self, "MESH", obj) - - # List objects create with rebool - lastSelected = [] - - for ActiveObj in selected_obj_list: - context.scene.cursor.location = CursorLocation - - if len(context.selected_objects) > 0: - bpy.ops.object.select_all(action='TOGGLE') - - # Select cut object - bpy.data.objects[self.CurrentObj.name].select_set(True) - context.view_layer.objects.active = self.CurrentObj - - bpy.ops.object.mode_set(mode='EDIT') - bpy.ops.mesh.select_all(action='SELECT') - bpy.ops.object.mode_set(mode='OBJECT') - - # Select object to cut - bpy.data.objects[ActiveObj.name].select_set(True) - context.view_layer.objects.active = ActiveObj - - bpy.ops.object.mode_set(mode='EDIT') - bpy.ops.mesh.select_all(action='DESELECT') - bpy.ops.object.mode_set(mode='OBJECT') - - # Boolean operation - if (self.shift is False) and (self.ForceRebool is False): - if self.ObjectMode or self.ProfileMode: - if self.BoolOps == self.union: - boolean_operation(bool_type="UNION") - else: - boolean_operation(bool_type="DIFFERENCE") - else: - boolean_operation(bool_type="DIFFERENCE") - - # Apply booleans - if self.dont_apply_boolean is False: - BMname = "CT_" + self.CurrentObj.name - for mb in ActiveObj.modifiers: - if (mb.type == 'BOOLEAN') and (mb.name == BMname): - try: - bpy.ops.object.modifier_apply(apply_as='DATA', modifier=BMname) - except: - bpy.ops.object.modifier_remove(modifier=BMname) - exc_type, exc_value, exc_traceback = sys.exc_info() - self.report({'ERROR'}, str(exc_value)) - - bpy.ops.object.select_all(action='TOGGLE') - else: - if self.ObjectMode or self.ProfileMode: - for mb in self.CurrentObj.modifiers: - if (mb.type == 'SOLIDIFY') and (mb.name == "CT_SOLIDIFY"): - try: - bpy.ops.object.modifier_apply(apply_as='DATA', modifier="CT_SOLIDIFY") - except: - exc_type, exc_value, exc_traceback = sys.exc_info() - self.report({'ERROR'}, str(exc_value)) - - # Rebool - Rebool(context, self) - - # Test if not empty object - if context.selected_objects[0]: - rebool_RT = context.selected_objects[0] - if len(rebool_RT.data.vertices) > 0: - # Create Bevel for new objects - CreateBevel(context, context.selected_objects[0]) - - UndoAdd(self, "REBOOL", context.selected_objects[0]) - - context.scene.cursor.location = ActiveObj.location - bpy.ops.object.origin_set(type='ORIGIN_CURSOR') - else: - bpy.ops.object.delete(use_global=False) - - context.scene.cursor.location = CursorLocation - - if self.ObjectMode: - context.view_layer.objects.active = self.ObjectBrush - if self.ProfileMode: - context.view_layer.objects.active = self.ProfileBrush - - if self.dont_apply_boolean is False: - # Apply booleans - BMname = "CT_" + self.CurrentObj.name - for mb in ActiveObj.modifiers: - if (mb.type == 'BOOLEAN') and (mb.name == BMname): - try: - bpy.ops.object.modifier_apply(apply_as='DATA', modifier=BMname) - except: - bpy.ops.object.modifier_remove(modifier=BMname) - exc_type, exc_value, exc_traceback = sys.exc_info() - self.report({'ERROR'}, str(exc_value)) - # Get new objects created with rebool operations - if len(context.selected_objects) > 0: - if self.shift is True: - # Get the last object selected - lastSelected.append(context.selected_objects[0]) - - context.scene.cursor.location = CursorLocation - - if self.dont_apply_boolean is False: - # Remove cut object - if (self.ObjectMode is False) and (self.ProfileMode is False): - if len(context.selected_objects) > 0: - bpy.ops.object.select_all(action='TOGGLE') - bpy.data.objects[self.CurrentObj.name].select_set(True) - bpy.ops.object.delete(use_global=False) - else: - if self.ObjectMode: - self.ObjectBrush.display_type = self.InitBrush['display_type'] - - if len(context.selected_objects) > 0: - bpy.ops.object.select_all(action='TOGGLE') - - # Select cut objects - for obj in lastSelected: - bpy.data.objects[obj.name].select_set(True) - - for ActiveObj in selected_obj_list: - bpy.data.objects[ActiveObj.name].select_set(True) - context.view_layer.objects.active = ActiveObj - # Update bevel - list_act_obj = context.selected_objects.copy() - if self.Auto_BevelUpdate: - update_bevel(context) - - # Re-select initial objects - bpy.ops.object.select_all(action='TOGGLE') - if self.ObjectMode: - # Re-select brush - self.ObjectBrush.select_set(True) - for ActiveObj in selected_obj_list: - bpy.data.objects[ActiveObj.name].select_set(True) - context.view_layer.objects.active = ActiveObj - - # If object has children, set "Wire" draw type - if self.ObjectBrush is not None: - if len(self.ObjectBrush.children) > 0: - self.ObjectBrush.display_type = "WIRE" - if self.ProfileMode: - self.ProfileBrush.display_type = "WIRE" - - if in_local_view: - bpy.ops.view3d.localview() - - # Reset variables - self.CutMode = False - self.mouse_path.clear() - self.mouse_path = [(0, 0), (0, 0)] - - self.ForceRebool = False - - # bpy.ops.mesh.customdata_custom_splitnormals_clear() + # Object creation + if event.type == self.carver_prefs.Key_Create and event.value == 'PRESS': + if self.ExclusiveCreateMode is False: + self.CreateMode = not self.CreateMode + + # Auto Bevel Update + if event.type == self.carver_prefs.Key_Update and event.value == 'PRESS': + self.Auto_BevelUpdate = not self.Auto_BevelUpdate + + # Boolean operation type + if event.type == self.carver_prefs.Key_Bool and event.value == 'PRESS': + if (self.ProfileMode is True) or (self.ObjectMode is True): + if self.BoolOps == self.difference: + self.BoolOps = self.union + else: + self.BoolOps = self.difference + + # Brush Mode + if event.type == self.carver_prefs.Key_Brush and event.value == 'PRESS': + self.dont_apply_boolean = False + if (self.ProfileMode is False) and (self.ObjectMode is False): + self.ProfileMode = True + else: + self.ProfileMode = False + if self.ObjectBrush is not None: + if self.ObjectMode is False: + self.ObjectMode = True + self.BrushSolidify = False + self.CList = self.OB_List + + Selection_Save_Restore(self) + context.scene.mesh_carver.nProfile = self.nProfil + else: + self.ObjectMode = False + else: + self.BrushSolidify = False + Selection_Save_Restore(self) + + if self.ProfileMode: + createMeshFromData(self) + self.ProfileBrush = bpy.data.objects["CT_Profil"] + Selection_Save(self) + self.BrushSolidify = True + + bpy.ops.object.select_all(action='TOGGLE') + self.ProfileBrush.select_set(True) + context.view_layer.objects.active = self.ProfileBrush + # Set xRay + self.ProfileBrush.show_in_front = True + + bpy.ops.object.modifier_add(type='SOLIDIFY') + context.object.modifiers["Solidify"].name = "CT_SOLIDIFY" + context.object.modifiers["CT_SOLIDIFY"].thickness = 0.1 + + Selection_Restore(self) + + self.CList = self.CurrentSelection + else: + if self.ObjectBrush is not None: + if self.ObjectMode is False: + if self.ObjectBrush is not None: + self.ObjectBrush.location = self.InitBrush['location'] + self.ObjectBrush.scale = self.InitBrush['scale'] + self.ObjectBrush.rotation_quaternion = self.InitBrush['rotation_quaternion'] + self.ObjectBrush.rotation_euler = self.InitBrush['rotation_euler'] + self.ObjectBrush.display_type = self.InitBrush['display_type'] + self.ObjectBrush.show_in_front = self.InitBrush['show_in_front'] + + #Store active and selected objects + Selection_Save(self) + + #Remove Carver modifier + self.BrushSolidify = False + bpy.ops.object.select_all(action='TOGGLE') + self.ObjectBrush.select_set(True) + context.view_layer.objects.active = self.ObjectBrush + bpy.ops.object.modifier_remove(modifier="CT_SOLIDIFY") + + #Restore selected and active object + Selection_Restore(self) + else: + if self.SolidifyPossible: + #Store active and selected objects + Selection_Save(self) + self.BrushSolidify = True + bpy.ops.object.select_all(action='TOGGLE') + self.ObjectBrush.select_set(True) + context.view_layer.objects.active = self.ObjectBrush + # Set xRay + self.ObjectBrush.show_in_front = True + bpy.ops.object.modifier_add(type='SOLIDIFY') + context.object.modifiers["Solidify"].name = "CT_SOLIDIFY" + context.object.modifiers["CT_SOLIDIFY"].thickness = 0.1 + + #Restore selected and active object + Selection_Restore(self) + + # Help display + if event.type == self.carver_prefs.Key_Help and event.value == 'PRESS': + self.AskHelp = not self.AskHelp + + # Instantiate object + if event.type == self.carver_prefs.Key_Instant and event.value == 'PRESS': + self.Instantiate = not self.Instantiate + + # Close polygonal shape + if event.type == self.carver_prefs.Key_Close and event.value == 'PRESS': + if self.CreateMode: + self.Closed = not self.Closed + + if event.type == self.carver_prefs.Key_Apply and event.value == 'PRESS': + self.dont_apply_boolean = not self.dont_apply_boolean + + # Scale object + if event.type == self.carver_prefs.Key_Scale and event.value == 'PRESS': + if self.ObjectScale is False: + self.mouse_region = event.mouse_region_x, event.mouse_region_y + self.ObjectScale = True + + # Grid : Snap on grid + if event.type == self.carver_prefs.Key_Snap and event.value == 'PRESS': + self.snap = not self.snap + + # Array : Add column + if event.type == 'UP_ARROW' and event.value == 'PRESS': + self.nbrow += 1 + update_grid(self, context) + + # Array : Delete column + elif event.type == 'DOWN_ARROW' and event.value == 'PRESS': + self.nbrow -= 1 + update_grid(self, context) + + # Array : Add row + elif event.type == 'RIGHT_ARROW' and event.value == 'PRESS': + self.nbcol += 1 + update_grid(self, context) + + # Array : Delete row + elif event.type == 'LEFT_ARROW' and event.value == 'PRESS': + self.nbcol -= 1 + update_grid(self, context) + + # Array : Scale gap between columns + if event.type == self.carver_prefs.Key_Gapy and event.value == 'PRESS': + if self.GridScaleX is False: + self.mouse_region = event.mouse_region_x, event.mouse_region_y + self.GridScaleX = True + + # Array : Scale gap between rows + if event.type == self.carver_prefs.Key_Gapx and event.value == 'PRESS': + if self.GridScaleY is False: + self.mouse_region = event.mouse_region_x, event.mouse_region_y + self.GridScaleY = True + + # Cursor depth or solidify pattern + if event.type == self.carver_prefs.Key_Depth and event.value == 'PRESS': + if (self.ObjectMode is False) and (self.ProfileMode is False): + self.snapCursor = not self.snapCursor + else: + # Solidify + + if (self.ObjectMode or self.ProfileMode) and (self.SolidifyPossible): + solidify = True + + if self.ObjectMode: + z = self.ObjectBrush.data.vertices[0].co.z + ErrorMarge = 0.01 + for v in self.ObjectBrush.data.vertices: + if abs(v.co.z - z) > ErrorMarge: + solidify = False + self.CarveDepth = True + self.mouse_region = event.mouse_region_x, event.mouse_region_y + break + + if solidify: + if self.ObjectMode: + for mb in self.ObjectBrush.modifiers: + if mb.type == 'SOLIDIFY': + AlreadySoldify = True + else: + for mb in self.ProfileBrush.modifiers: + if mb.type == 'SOLIDIFY': + AlreadySoldify = True + + if AlreadySoldify is False: + Selection_Save(self) + self.BrushSolidify = True + + bpy.ops.object.select_all(action='TOGGLE') + if self.ObjectMode: + self.ObjectBrush.select_set(True) + context.view_layer.objects.active = self.ObjectBrush + # Active le xray + self.ObjectBrush.show_in_front = True + else: + self.ProfileBrush.select_set(True) + context.view_layer.objects.active = self.ProfileBrush + # Active le xray + self.ProfileBrush.show_in_front = True + + bpy.ops.object.modifier_add(type='SOLIDIFY') + context.object.modifiers["Solidify"].name = "CT_SOLIDIFY" + + context.object.modifiers["CT_SOLIDIFY"].thickness = 0.1 + + Selection_Restore(self) + + self.WidthSolidify = not self.WidthSolidify + self.mouse_region = event.mouse_region_x, event.mouse_region_y + + if event.type == self.carver_prefs.Key_BrushDepth and event.value == 'PRESS': + if self.ObjectMode: + self.CarveDepth = False + + self.BrushDepth = True + self.mouse_region = event.mouse_region_x, event.mouse_region_y + + # Random rotation + if event.type == 'R' and event.value == 'PRESS': + self.RandomRotation = not self.RandomRotation + + # Undo + if event.type == 'Z' and event.value == 'PRESS': + if self.ctrl: + if (self.CutType == self.line) and (self.CutMode): + if len(self.mouse_path) > 1: + self.mouse_path[len(self.mouse_path) - 1:] = [] + else: + Undo(self) + + # Mouse move + if event.type == 'MOUSEMOVE' : + if self.ObjectMode or self.ProfileMode: + fac = 50.0 + if self.shift: + fac = 500.0 + if self.WidthSolidify: + if self.ObjectMode: + bpy.data.objects[self.ObjectBrush.name].modifiers[ + "CT_SOLIDIFY"].thickness += (event.mouse_region_x - self.mouse_region[0]) / fac + elif self.ProfileMode: + bpy.data.objects[self.ProfileBrush.name].modifiers[ + "CT_SOLIDIFY"].thickness += (event.mouse_region_x - self.mouse_region[0]) / fac + self.mouse_region = event.mouse_region_x, event.mouse_region_y + elif self.CarveDepth: + for v in self.ObjectBrush.data.vertices: + v.co.z += (event.mouse_region_x - self.mouse_region[0]) / fac + self.mouse_region = event.mouse_region_x, event.mouse_region_y + elif self.BrushDepth: + self.BrushDepthOffset += (event.mouse_region_x - self.mouse_region[0]) / fac + self.mouse_region = event.mouse_region_x, event.mouse_region_y + else: + if (self.GridScaleX): + self.gapx += (event.mouse_region_x - self.mouse_region[0]) / 50 + self.mouse_region = event.mouse_region_x, event.mouse_region_y + update_grid(self, context) + return {'RUNNING_MODAL'} + + elif (self.GridScaleY): + self.gapy += (event.mouse_region_x - self.mouse_region[0]) / 50 + self.mouse_region = event.mouse_region_x, event.mouse_region_y + update_grid(self, context) + return {'RUNNING_MODAL'} + + elif self.ObjectScale: + self.ascale = -(event.mouse_region_x - self.mouse_region[0]) + self.mouse_region = event.mouse_region_x, event.mouse_region_y + + if self.ObjectMode: + self.ObjectBrush.scale.x -= float(self.ascale) / 150.0 + if self.ObjectBrush.scale.x <= 0.0: + self.ObjectBrush.scale.x = 0.0 + self.ObjectBrush.scale.y -= float(self.ascale) / 150.0 + if self.ObjectBrush.scale.y <= 0.0: + self.ObjectBrush.scale.y = 0.0 + self.ObjectBrush.scale.z -= float(self.ascale) / 150.0 + if self.ObjectBrush.scale.z <= 0.0: + self.ObjectBrush.scale.z = 0.0 + + elif self.ProfileMode: + if self.ProfileBrush is not None: + self.ProfileBrush.scale.x -= float(self.ascale) / 150.0 + self.ProfileBrush.scale.y -= float(self.ascale) / 150.0 + self.ProfileBrush.scale.z -= float(self.ascale) / 150.0 + else: + if self.LMB: + if self.ctrl: + self.aRotZ = - \ + ((int((event.mouse_region_x - self.xSavMouse) / 10.0) * PI / 4.0) * 25.0) + else: + self.aRotZ -= event.mouse_region_x - self.mouse_region[0] + self.ascale = 0.0 + + self.mouse_region = event.mouse_region_x, event.mouse_region_y + else: + target_hit, target_normal, target_eul_rotation = Pick(context, event, self) + if target_hit is not None: + self.ShowCursor = True + up_vector = Vector((0.0, 0.0, 1.0)) + quat_rot_axis = rot_axis_quat(up_vector, target_normal) + self.quat_rot = target_eul_rotation @ quat_rot_axis + MoveCursor(quat_rot_axis, target_hit, self) + self.SavCurLoc = target_hit + if self.ctrl: + if self.SavMousePos is not None: + xEcart = abs(self.SavMousePos.x - self.SavCurLoc.x) + yEcart = abs(self.SavMousePos.y - self.SavCurLoc.y) + zEcart = abs(self.SavMousePos.z - self.SavCurLoc.z) + if (xEcart > yEcart) and (xEcart > zEcart): + self.CurLoc = Vector( + (target_hit.x, self.SavMousePos.y, self.SavMousePos.z)) + if (yEcart > xEcart) and (yEcart > zEcart): + self.CurLoc = Vector( + (self.SavMousePos.x, target_hit.y, self.SavMousePos.z)) + if (zEcart > xEcart) and (zEcart > yEcart): + self.CurLoc = Vector( + (self.SavMousePos.x, self.SavMousePos.y, target_hit.z)) + else: + self.CurLoc = target_hit + else: + self.CurLoc = target_hit + else: + if self.CutMode: + if self.alt is False: + if self.ctrl : + # Find the closest position on the overlay grid and snap the mouse on it + # Draw a mini grid around the cursor + mouse_pos = [[event.mouse_region_x, event.mouse_region_y]] + Snap_Cursor(self, context, event, mouse_pos) + + else: + if len(self.mouse_path) > 0: + self.mouse_path[len(self.mouse_path) - + 1] = (event.mouse_region_x, event.mouse_region_y) + else: + # [ALT] press, update position + self.xpos += (event.mouse_region_x - self.last_mouse_region_x) + self.ypos += (event.mouse_region_y - self.last_mouse_region_y) + + self.last_mouse_region_x = event.mouse_region_x + self.last_mouse_region_y = event.mouse_region_y + + elif event.type == 'LEFTMOUSE' and event.value == 'PRESS': + if self.ObjectMode or self.ProfileMode: + if self.LMB is False: + target_hit, target_normal, target_eul_rotation = Pick(context, event, self) + if target_hit is not None: + up_vector = Vector((0.0, 0.0, 1.0)) + self.quat_rot_axis = rot_axis_quat(up_vector, target_normal) + self.quat_rot = target_eul_rotation @ self.quat_rot_axis + self.mouse_region = event.mouse_region_x, event.mouse_region_y + self.xSavMouse = event.mouse_region_x + + if self.ctrl: + self.nRotZ = int((self.aRotZ / 25.0) / (PI / 4.0)) + self.aRotZ = self.nRotZ * (PI / 4.0) * 25.0 + + self.LMB = True + + # LEFTMOUSE + elif event.type == 'LEFTMOUSE' and event.value == 'RELEASE' and self.in_view_3d: + if self.ObjectMode or self.ProfileMode: + # Rotation and scale + self.LMB = False + if self.ObjectScale is True: + self.ObjectScale = False + + if self.GridScaleX is True: + self.GridScaleX = False + + if self.GridScaleY is True: + self.GridScaleY = False + + if self.WidthSolidify: + self.WidthSolidify = False + + if self.CarveDepth is True: + self.CarveDepth = False + + if self.BrushDepth is True: + self.BrushDepth = False + + else: + if self.CutMode is False: + if self.ctrl: + Picking(context, event) + + else: + + if self.CutType == self.line: + if self.CutMode is False: + self.mouse_path.clear() + self.mouse_path.append((event.mouse_region_x, event.mouse_region_y)) + self.mouse_path.append((event.mouse_region_x, event.mouse_region_y)) + else: + self.mouse_path[0] = (event.mouse_region_x, event.mouse_region_y) + self.mouse_path[1] = (event.mouse_region_x, event.mouse_region_y) + self.CutMode = True + else: + if self.CutType != self.line: + # Cut creation + if self.CutType == self.rectangle: + CreateCutSquare(self, context) + if self.CutType == self.circle: + CreateCutCircle(self, context) + if self.CutType == self.line: + CreateCutLine(self, context) + + if self.CreateMode: + # Object creation + self.CreateGeometry() + bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') + # Depth Cursor + context.scene.mesh_carver.DepthCursor = self.snapCursor + # Instantiate object + context.scene.mesh_carver.OInstanciate = self.Instantiate + # Random rotation + context.scene.mesh_carver.ORandom = self.RandomRotation + # Apply operation + context.scene.mesh_carver.DontApply = self.dont_apply_boolean + + # if Object mode, set initiale state + if self.ObjectBrush is not None: + self.ObjectBrush.location = self.InitBrush['location'] + self.ObjectBrush.scale = self.InitBrush['scale'] + self.ObjectBrush.rotation_quaternion = self.InitBrush['rotation_quaternion'] + self.ObjectBrush.rotation_euler = self.InitBrush['rotation_euler'] + self.ObjectBrush.display_type = self.InitBrush['display_type'] + self.ObjectBrush.show_in_front = self.InitBrush['show_in_front'] + + # remove solidify + Selection_Save(self) + self.BrushSolidify = False + + bpy.ops.object.select_all(action='TOGGLE') + self.ObjectBrush.select_set(True) + context.view_layer.objects.active = self.ObjectBrush + + bpy.ops.object.modifier_remove(modifier="CT_SOLIDIFY") + + Selection_Restore(self) + + context.scene.mesh_carver.nProfile = self.nProfil + + return {'FINISHED'} + else: + self.Cut() + UndoListUpdate(self) + else: + # Line + self.mouse_path.append((event.mouse_region_x, event.mouse_region_y)) + + # Change brush profil or circle subdivisions + elif (event.type == 'COMMA' and event.value == 'PRESS') or \ + (event.type == self.carver_prefs.Key_Subrem and event.value == 'PRESS'): + # Brush profil + if self.ProfileMode: + self.nProfil += 1 + if self.nProfil >= self.MaxProfil: + self.nProfil = 0 + createMeshFromData(self) + # Circle subdivisions + if self.CutType == self.circle: + self.step += 1 + if self.step >= len(self.stepAngle): + self.step = len(self.stepAngle) - 1 + # Change brush profil or circle subdivisions + elif (event.type == 'PERIOD' and event.value == 'PRESS') or \ + (event.type == self.carver_prefs.Key_Subadd and event.value == 'PRESS'): + # Brush profil + if self.ProfileMode: + self.nProfil -= 1 + if self.nProfil < 0: + self.nProfil = self.MaxProfil - 1 + createMeshFromData(self) + # Circle subdivisions + if self.CutType == self.circle: + if self.step > 0: + self.step -= 1 + # Quit + elif event.type in {'RIGHTMOUSE', 'ESC'}: + # Depth Cursor + context.scene.mesh_carver.DepthCursor = self.snapCursor + # Instantiate object + context.scene.mesh_carver.OInstanciate = self.Instantiate + # Random Rotation + context.scene.mesh_carver.ORandom = self.RandomRotation + # Apply boolean operation + context.scene.mesh_carver.DontApply = self.dont_apply_boolean + + # Reset Object + if self.ObjectBrush is not None: + self.ObjectBrush.location = self.InitBrush['location'] + self.ObjectBrush.scale = self.InitBrush['scale'] + self.ObjectBrush.rotation_quaternion = self.InitBrush['rotation_quaternion'] + self.ObjectBrush.rotation_euler = self.InitBrush['rotation_euler'] + self.ObjectBrush.display_type = self.InitBrush['display_type'] + self.ObjectBrush.show_in_front = self.InitBrush['show_in_front'] + + # Remove solidify modifier + Selection_Save(self) + self.BrushSolidify = False + + bpy.ops.object.select_all(action='TOGGLE') + self.ObjectBrush.select_set(True) + context.view_layer.objects.active = self.ObjectBrush + + bpy.ops.object.modifier_remove(modifier="CT_SOLIDIFY") + bpy.ops.object.select_all(action='TOGGLE') + + Selection_Restore(self) + + Selection_Save_Restore(self) + context.view_layer.objects.active = self.CurrentActive + context.scene.mesh_carver.nProfile = self.nProfil + + bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') + + # Remove Copy Object Brush + if bpy.data.objects.get("CarverBrushCopy") is not None: + brush = bpy.data.objects["CarverBrushCopy"] + self.ObjectBrush.data = bpy.data.meshes[brush.data.name] + bpy.ops.object.select_all(action='DESELECT') + bpy.data.objects["CarverBrushCopy"].select_set(True) + bpy.ops.object.delete() + + return {'FINISHED'} + + return {'RUNNING_MODAL'} + + except: + print("\n[Carver MT ERROR]\n") + import traceback + traceback.print_exc() + + context.window.cursor_modal_set("DEFAULT") + context.area.header_text_set(None) + bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') + + self.report({'WARNING'}, + "Operation finished. Failure during Carving (Check the console for more info)") + + return {'FINISHED'} + + def cancel(self, context): + # Note: used to prevent memory leaks on quitting Blender while the modal operator + # is still running, gets called on return {"CANCELLED"} + bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') + + def invoke(self, context, event): + if context.area.type != 'VIEW_3D': + self.report({'WARNING'}, + "View3D not found or not currently active. Operation Cancelled") + self.cancel(context) + return {'CANCELLED'} + + # test if some other object types are selected that are not meshes + for obj in context.selected_objects: + if obj.type != "MESH": + self.report({'WARNING'}, + "Some selected objects are not of the Mesh type. Operation Cancelled") + self.cancel(context) + return {'CANCELLED'} + + if context.mode == 'EDIT_MESH': + bpy.ops.object.mode_set(mode='OBJECT') + + #Load the Carver preferences + self.carver_prefs = bpy.context.preferences.addons[__package__].preferences + + # Get default patterns + self.Profils = [] + for p in Profils: + self.Profils.append((p[0], p[1], p[2], p[3])) + + for o in context.scene.objects: + if not o.name.startswith(self.carver_prefs.ProfilePrefix): + continue + # In-scene profiles may have changed, remove them to refresh + for m in bpy.data.meshes: + if m.name.startswith(self.carver_prefs.ProfilePrefix): + bpy.data.meshes.remove(m) + + vertices = [] + for v in o.data.vertices: + vertices.append((v.co.x, v.co.y, v.co.z)) + + faces = [] + for f in o.data.polygons: + face = [] + for v in f.vertices: + face.append(v) + + faces.append(face) + + self.Profils.append( + (o.name, + Vector((o.location.x, o.location.y, o.location.z)), + vertices, faces) + ) + + self.nProfil = context.scene.mesh_carver.nProfile + self.MaxProfil = len(self.Profils) + + + # reset selected profile if last profile exceeds length of array + if self.nProfil >= self.MaxProfil: + self.nProfil = context.scene.mesh_carver.nProfile = 0 + + if len(context.selected_objects) > 1: + self.ObjectBrush = context.active_object + + # Copy the brush object + ob = bpy.data.objects.new("CarverBrushCopy", context.object.data.copy()) + ob.location = self.ObjectBrush.location + context.collection.objects.link(ob) + context.view_layer.update() + + # Save default variables + self.InitBrush['location'] = self.ObjectBrush.location.copy() + self.InitBrush['scale'] = self.ObjectBrush.scale.copy() + self.InitBrush['rotation_quaternion'] = self.ObjectBrush.rotation_quaternion.copy() + self.InitBrush['rotation_euler'] = self.ObjectBrush.rotation_euler.copy() + self.InitBrush['display_type'] = self.ObjectBrush.display_type + self.InitBrush['show_in_front'] = self.ObjectBrush.show_in_front + + # Test if flat object + z = self.ObjectBrush.data.vertices[0].co.z + ErrorMarge = 0.01 + self.SolidifyPossible = True + for v in self.ObjectBrush.data.vertices: + if abs(v.co.z - z) > ErrorMarge: + self.SolidifyPossible = False + break + + self.CList = [] + self.OPList = [] + self.RList = [] + self.OB_List = [] + + for obj in context.selected_objects: + if obj != self.ObjectBrush: + self.OB_List.append(obj) + + # Left button + self.LMB = False + + # Undo Variables + self.undo_index = 0 + self.undo_limit = context.preferences.edit.undo_steps + self.undo_list = [] + + # Boolean operations type + self.BooleanType = 0 + + self.UList = [] + self.UList_Index = -1 + self.UndoOps = [] + + context.window_manager.modal_handler_add(self) + return {'RUNNING_MODAL'} + + #Get the region area where the operator is used + def check_region(self,context,event): + if context.area != None: + if context.area.type == "VIEW_3D" : + for region in context.area.regions: + if region.type == "TOOLS": + t_panel = region + elif region.type == "UI": + ui_panel = region + + view_3d_region_x = Vector((context.area.x + t_panel.width, context.area.x + context.area.width - ui_panel.width)) + view_3d_region_y = Vector((context.region.y, context.region.y+context.region.height)) + + if (event.mouse_x > view_3d_region_x[0] and event.mouse_x < view_3d_region_x[1] \ + and event.mouse_y > view_3d_region_y[0] and event.mouse_y < view_3d_region_y[1]): + self.in_view_3d = True + else: + self.in_view_3d = False + else: + self.in_view_3d = False + + def CreateGeometry(self): + context = bpy.context + in_local_view = False + + for area in context.screen.areas: + if area.type == 'VIEW_3D': + if area.spaces[0].local_view is not None: + in_local_view = True + + if in_local_view: + bpy.ops.view3d.localview() + + if self.ExclusiveCreateMode: + # Default width + objBBDiagonal = 0.5 + else: + ActiveObj = self.CurrentSelection[0] + if ActiveObj is not None: + # Object dimensions + objBBDiagonal = objDiagonal(ActiveObj) / 4 + subdivisions = 2 + + if len(context.selected_objects) > 0: + bpy.ops.object.select_all(action='TOGGLE') + + context.view_layer.objects.active = self.CurrentObj + + bpy.data.objects[self.CurrentObj.name].select_set(True) + bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY') + + bpy.ops.object.mode_set(mode='EDIT') + bpy.ops.mesh.select_all(action='SELECT') + bpy.ops.mesh.select_mode(type="EDGE") + if self.snapCursor is False: + bpy.ops.transform.translate(value=self.ViewVector * objBBDiagonal * subdivisions) + bpy.ops.mesh.extrude_region_move( + TRANSFORM_OT_translate={"value": -self.ViewVector * objBBDiagonal * subdivisions * 2}) + + bpy.ops.mesh.select_all(action='SELECT') + bpy.ops.mesh.normals_make_consistent() + bpy.ops.object.mode_set(mode='OBJECT') + + saved_location_0 = context.scene.cursor.location.copy() + bpy.ops.view3d.snap_cursor_to_active() + saved_location = context.scene.cursor.location.copy() + bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) + context.scene.cursor.location = saved_location + bpy.ops.object.origin_set(type='ORIGIN_CURSOR') + context.scene.cursor.location = saved_location_0 + + bpy.data.objects[self.CurrentObj.name].select_set(True) + bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY') + + for o in self.all_sel_obj_list: + bpy.data.objects[o.name].select_set(True) + + if in_local_view: + bpy.ops.view3d.localview() + + self.CutMode = False + self.mouse_path.clear() + self.mouse_path = [(0, 0), (0, 0)] + + def Cut(self): + context = bpy.context + + # Local view ? + in_local_view = False + for area in context.screen.areas: + if area.type == 'VIEW_3D': + if area.spaces[0].local_view is not None: + in_local_view = True + + if in_local_view: + bpy.ops.view3d.localview() + + # Save cursor position + CursorLocation = context.scene.cursor.location.copy() + + #List of selected objects + selected_obj_list = [] + + #Cut Mode with line + if (self.ObjectMode is False) and (self.ProfileMode is False): + + #Compute the bounding Box + objBBDiagonal = objDiagonal(self.CurrentSelection[0]) + if self.dont_apply_boolean: + subdivisions = 1 + else: + subdivisions = 32 + + # Get selected objects + selected_obj_list = context.selected_objects.copy() + + bpy.ops.object.select_all(action='TOGGLE') + + context.view_layer.objects.active = self.CurrentObj + + bpy.data.objects[self.CurrentObj.name].select_set(True) + bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY') + + bpy.ops.object.mode_set(mode='EDIT') + bpy.ops.mesh.select_all(action='SELECT') + bpy.ops.mesh.select_mode(type="EDGE") + #Translate the created mesh away from the view + if (self.snapCursor is False) or (self.ForceRebool): + bpy.ops.transform.translate(value=self.ViewVector * objBBDiagonal * subdivisions) + #Extrude the mesh region and move the result + bpy.ops.mesh.extrude_region_move( + TRANSFORM_OT_translate={"value": -self.ViewVector * objBBDiagonal * subdivisions * 2}) + bpy.ops.mesh.select_all(action='SELECT') + bpy.ops.mesh.normals_make_consistent() + bpy.ops.object.mode_set(mode='OBJECT') + else: + # Create list + if self.ObjectMode: + for o in self.CurrentSelection: + if o != self.ObjectBrush: + selected_obj_list.append(o) + self.CurrentObj = self.ObjectBrush + else: + selected_obj_list = self.CurrentSelection + self.CurrentObj = self.ProfileBrush + + for obj in self.CurrentSelection: + UndoAdd(self, "MESH", obj) + + # List objects create with rebool + lastSelected = [] + + for ActiveObj in selected_obj_list: + context.scene.cursor.location = CursorLocation + + if len(context.selected_objects) > 0: + bpy.ops.object.select_all(action='TOGGLE') + + # Select cut object + bpy.data.objects[self.CurrentObj.name].select_set(True) + context.view_layer.objects.active = self.CurrentObj + + bpy.ops.object.mode_set(mode='EDIT') + bpy.ops.mesh.select_all(action='SELECT') + bpy.ops.object.mode_set(mode='OBJECT') + + # Select object to cut + bpy.data.objects[ActiveObj.name].select_set(True) + context.view_layer.objects.active = ActiveObj + + bpy.ops.object.mode_set(mode='EDIT') + bpy.ops.mesh.select_all(action='DESELECT') + bpy.ops.object.mode_set(mode='OBJECT') + + # Boolean operation + if (self.shift is False) and (self.ForceRebool is False): + if self.ObjectMode or self.ProfileMode: + if self.BoolOps == self.union: + boolean_operation(bool_type="UNION") + else: + boolean_operation(bool_type="DIFFERENCE") + else: + boolean_operation(bool_type="DIFFERENCE") + + # Apply booleans + if self.dont_apply_boolean is False: + BMname = "CT_" + self.CurrentObj.name + for mb in ActiveObj.modifiers: + if (mb.type == 'BOOLEAN') and (mb.name == BMname): + try: + bpy.ops.object.modifier_apply(apply_as='DATA', modifier=BMname) + except: + bpy.ops.object.modifier_remove(modifier=BMname) + exc_type, exc_value, exc_traceback = sys.exc_info() + self.report({'ERROR'}, str(exc_value)) + + bpy.ops.object.select_all(action='TOGGLE') + else: + if self.ObjectMode or self.ProfileMode: + for mb in self.CurrentObj.modifiers: + if (mb.type == 'SOLIDIFY') and (mb.name == "CT_SOLIDIFY"): + try: + bpy.ops.object.modifier_apply(apply_as='DATA', modifier="CT_SOLIDIFY") + except: + exc_type, exc_value, exc_traceback = sys.exc_info() + self.report({'ERROR'}, str(exc_value)) + + # Rebool + Rebool(context, self) + + # Test if not empty object + if context.selected_objects[0]: + rebool_RT = context.selected_objects[0] + if len(rebool_RT.data.vertices) > 0: + # Create Bevel for new objects + CreateBevel(context, context.selected_objects[0]) + + UndoAdd(self, "REBOOL", context.selected_objects[0]) + + context.scene.cursor.location = ActiveObj.location + bpy.ops.object.origin_set(type='ORIGIN_CURSOR') + else: + bpy.ops.object.delete(use_global=False) + + context.scene.cursor.location = CursorLocation + + if self.ObjectMode: + context.view_layer.objects.active = self.ObjectBrush + if self.ProfileMode: + context.view_layer.objects.active = self.ProfileBrush + + if self.dont_apply_boolean is False: + # Apply booleans + BMname = "CT_" + self.CurrentObj.name + for mb in ActiveObj.modifiers: + if (mb.type == 'BOOLEAN') and (mb.name == BMname): + try: + bpy.ops.object.modifier_apply(apply_as='DATA', modifier=BMname) + except: + bpy.ops.object.modifier_remove(modifier=BMname) + exc_type, exc_value, exc_traceback = sys.exc_info() + self.report({'ERROR'}, str(exc_value)) + # Get new objects created with rebool operations + if len(context.selected_objects) > 0: + if self.shift is True: + # Get the last object selected + lastSelected.append(context.selected_objects[0]) + + context.scene.cursor.location = CursorLocation + + if self.dont_apply_boolean is False: + # Remove cut object + if (self.ObjectMode is False) and (self.ProfileMode is False): + if len(context.selected_objects) > 0: + bpy.ops.object.select_all(action='TOGGLE') + bpy.data.objects[self.CurrentObj.name].select_set(True) + bpy.ops.object.delete(use_global=False) + else: + if self.ObjectMode: + self.ObjectBrush.display_type = self.InitBrush['display_type'] + + if len(context.selected_objects) > 0: + bpy.ops.object.select_all(action='TOGGLE') + + # Select cut objects + for obj in lastSelected: + bpy.data.objects[obj.name].select_set(True) + + for ActiveObj in selected_obj_list: + bpy.data.objects[ActiveObj.name].select_set(True) + context.view_layer.objects.active = ActiveObj + # Update bevel + list_act_obj = context.selected_objects.copy() + if self.Auto_BevelUpdate: + update_bevel(context) + + # Re-select initial objects + bpy.ops.object.select_all(action='TOGGLE') + if self.ObjectMode: + # Re-select brush + self.ObjectBrush.select_set(True) + for ActiveObj in selected_obj_list: + bpy.data.objects[ActiveObj.name].select_set(True) + context.view_layer.objects.active = ActiveObj + + # If object has children, set "Wire" draw type + if self.ObjectBrush is not None: + if len(self.ObjectBrush.children) > 0: + self.ObjectBrush.display_type = "WIRE" + if self.ProfileMode: + self.ProfileBrush.display_type = "WIRE" + + if in_local_view: + bpy.ops.view3d.localview() + + # Reset variables + self.CutMode = False + self.mouse_path.clear() + self.mouse_path = [(0, 0), (0, 0)] + + self.ForceRebool = False + + # bpy.ops.mesh.customdata_custom_splitnormals_clear() class CarverProperties(bpy.types.PropertyGroup): - DepthCursor: BoolProperty( - name="DepthCursor", - default=False - ) - OInstanciate: BoolProperty( - name="Obj_Instantiate", - default=False - ) - ORandom: BoolProperty( - name="Random_Rotation", - default=False - ) - DontApply: BoolProperty( - name="Dont_Apply", - default=False - ) - nProfile: IntProperty( - name="Num_Profile", - default=0 - ) + DepthCursor: BoolProperty( + name="DepthCursor", + default=False + ) + OInstanciate: BoolProperty( + name="Obj_Instantiate", + default=False + ) + ORandom: BoolProperty( + name="Random_Rotation", + default=False + ) + DontApply: BoolProperty( + name="Dont_Apply", + default=False + ) + nProfile: IntProperty( + name="Num_Profile", + default=0 + ) def register(): - from bpy.utils import register_class - bpy.utils.register_class(CARVER_OT_operator) - bpy.utils.register_class(CarverProperties) - bpy.types.Scene.mesh_carver = bpy.props.PointerProperty(type=CarverProperties) + from bpy.utils import register_class + bpy.utils.register_class(CARVER_OT_operator) + bpy.utils.register_class(CarverProperties) + bpy.types.Scene.mesh_carver = bpy.props.PointerProperty(type=CarverProperties) def unregister(): - from bpy.utils import unregister_class - bpy.utils.unregister_class(CarverProperties) - bpy.utils.unregister_class(CARVER_OT_operator) - del bpy.types.Scene.mesh_carver + from bpy.utils import unregister_class + bpy.utils.unregister_class(CarverProperties) + bpy.utils.unregister_class(CARVER_OT_operator) + del bpy.types.Scene.mesh_carver |