1import bpy
2import bpy_extras
3import sys
4from bpy.props import (
5    BoolProperty,
6    IntProperty,
7    PointerProperty,
8    StringProperty,
9    EnumProperty,
10    )
11from mathutils import (
12    Vector,
13    )
14
15from bpy_extras.view3d_utils import (
16    region_2d_to_vector_3d,
17    region_2d_to_origin_3d,
18    region_2d_to_location_3d,
19    location_3d_to_region_2d,
20)
21from .carver_profils import (
22    Profils
23    )
24
25from .carver_utils import (
26    duplicateObject,
27    UndoListUpdate,
28    createMeshFromData,
29    SelectObject,
30    Selection_Save_Restore,
31    Selection_Save,
32    Selection_Restore,
33    update_grid,
34    objDiagonal,
35    Undo,
36    UndoAdd,
37    Pick,
38    rot_axis_quat,
39    MoveCursor,
40    Picking,
41    CreateCutSquare,
42    CreateCutCircle,
43    CreateCutLine,
44    boolean_operation,
45    update_bevel,
46    CreateBevel,
47    Rebool,
48    Snap_Cursor,
49    )
50
51from .carver_draw import draw_callback_px
52
53# Modal Operator
54class CARVER_OT_operator(bpy.types.Operator):
55    bl_idname = "carver.operator"
56    bl_label = "Carver"
57    bl_description = "Cut or create Meshes in Object mode"
58    bl_options = {'REGISTER', 'UNDO'}
59
60    def __init__(self):
61        context = bpy.context
62        # Carve mode: Cut, Object, Profile
63        self.CutMode = False
64        self.CreateMode = False
65        self.ObjectMode = False
66        self.ProfileMode = False
67
68        # Create mode
69        self.ExclusiveCreateMode = False
70        if len(context.selected_objects) == 0:
71            self.ExclusiveCreateMode = True
72            self.CreateMode = True
73
74        # Cut type (Rectangle, Circle, Line)
75        self.rectangle = 0
76        self.line = 1
77        self.circle = 2
78
79        # Cut Rectangle coordinates
80        self.rectangle_coord = []
81
82        # Selected type of cut
83        self.CutType = 0
84
85        # Boolean operation
86        self.difference = 0
87        self.union = 1
88
89        self.BoolOps = self.difference
90
91        self.CurrentSelection = context.selected_objects.copy()
92        self.CurrentActive = context.active_object
93        self.all_sel_obj_list = context.selected_objects.copy()
94        self.save_active_obj = None
95
96        args = (self, context)
97        self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL')
98
99        self.mouse_path = [(0, 0), (0, 0)]
100
101        # Keyboard event
102        self.shift = False
103        self.ctrl = False
104        self.alt = False
105
106        self.dont_apply_boolean = context.scene.mesh_carver.DontApply
107        self.Auto_BevelUpdate = True
108
109        # Circle variables
110        self.stepAngle = [2, 4, 5, 6, 9, 10, 15, 20, 30, 40, 45, 60, 72, 90]
111        self.step = 4
112
113        # Primitives Position
114        self.xpos = 0
115        self.ypos = 0
116        self.InitPosition = False
117
118        # Close polygonal shape
119        self.Closed = False
120
121        # Depth Cursor
122        self.snapCursor = context.scene.mesh_carver.DepthCursor
123
124        # Help
125        self.AskHelp = False
126
127        # Working object
128        self.OpsObj = context.active_object
129
130        # Rebool forced (cut line)
131        self.ForceRebool = False
132
133        self.ViewVector = Vector()
134        self.CurrentObj = None
135
136        # Brush
137        self.BrushSolidify = False
138        self.WidthSolidify = False
139        self.CarveDepth = False
140        self.BrushDepth = False
141        self.BrushDepthOffset = 0.0
142        self.snap = False
143
144        self.ObjectScale = False
145
146        #Init create circle primitive
147        self.CLR_C = []
148
149        # Cursor location
150        self.CurLoc = Vector((0.0, 0.0, 0.0))
151        self.SavCurLoc = Vector((0.0, 0.0, 0.0))
152
153        # Mouse region
154        self.mouse_region = -1, -1
155        self.SavMousePos = None
156        self.xSavMouse = 0
157
158        # Scale, rotate object
159        self.ascale = 0
160        self.aRotZ = 0
161        self.nRotZ = 0
162        self.quat_rot_axis = None
163        self.quat_rot = None
164
165        self.RandomRotation = context.scene.mesh_carver.ORandom
166
167        self.ShowCursor = True
168
169        self.Instantiate = context.scene.mesh_carver.OInstanciate
170
171        self.ProfileBrush = None
172        self.ObjectBrush = None
173
174        self.InitBrush = {
175        'location' : None,
176        'scale' : None,
177        'rotation_quaternion' : None,
178        'rotation_euler' : None,
179        'display_type' : 'WIRE',
180        'show_in_front' : False
181        }
182
183        # Array variables
184        self.nbcol = 1
185        self.nbrow = 1
186        self.gapx = 0
187        self.gapy = 0
188        self.scale_x = 1
189        self.scale_y = 1
190        self.GridScaleX = False
191        self.GridScaleY = False
192
193    @classmethod
194    def poll(cls, context):
195        ob = None
196        if len(context.selected_objects) > 0:
197            ob = context.selected_objects[0]
198        # Test if selected object or none (for create mode)
199        return (
200            (ob and ob.type == 'MESH' and context.mode == 'OBJECT') or
201            (context.mode == 'OBJECT' and ob is None) or
202            (context.mode == 'EDIT_MESH'))
203
204    def modal(self, context, event):
205        PI = 3.14156
206        region_types = {'WINDOW', 'UI'}
207        win = context.window
208
209        # Find the limit of the view3d region
210        self.check_region(context,event)
211
212        for area in win.screen.areas:
213            if area.type == 'VIEW_3D':
214                for region in area.regions:
215                    if not region_types or region.type in region_types:
216                        region.tag_redraw()
217
218        # Change the snap increment value using the wheel mouse
219        if self.CutMode:
220            if self.alt is False:
221                if self.ctrl and (self.CutType in (self.line, self.rectangle)):
222                    # Get the VIEW3D area
223                    for i, a in enumerate(context.screen.areas):
224                        if a.type == 'VIEW_3D':
225                            space = context.screen.areas[i].spaces.active
226                    grid_scale = space.overlay.grid_scale
227                    grid_subdivisions = space.overlay.grid_subdivisions
228
229                    if event.type == 'WHEELUPMOUSE':
230                         space.overlay.grid_subdivisions += 1
231                    elif event.type == 'WHEELDOWNMOUSE':
232                         space.overlay.grid_subdivisions -= 1
233
234        if event.type in {
235                'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE',
236                'NUMPAD_1', 'NUMPAD_2', 'NUMPAD_3', 'NUMPAD_4', 'NUMPAD_6',
237                'NUMPAD_7', 'NUMPAD_8', 'NUMPAD_9', 'NUMPAD_5'}:
238            return {'PASS_THROUGH'}
239
240        try:
241            # [Shift]
242            self.shift = True if event.shift else False
243
244            # [Ctrl]
245            self.ctrl = True if event.ctrl else False
246
247            # [Alt]
248            self.alt = False
249
250            # [Alt] press : Init position variable before moving the cut brush with LMB
251            if event.alt:
252                if self.InitPosition is False:
253                    self.xpos = 0
254                    self.ypos = 0
255                    self.last_mouse_region_x = event.mouse_region_x
256                    self.last_mouse_region_y = event.mouse_region_y
257                    self.InitPosition = True
258                self.alt = True
259
260            # [Alt] release : update the coordinates
261            if self.InitPosition and self.alt is False:
262                for i in range(0, len(self.mouse_path)):
263                    l = list(self.mouse_path[i])
264                    l[0] += self.xpos
265                    l[1] += self.ypos
266                    self.mouse_path[i] = tuple(l)
267
268                self.xpos = self.ypos = 0
269                self.InitPosition = False
270
271            if event.type == 'SPACE' and event.value == 'PRESS':
272                # If object or profile mode is TRUE : Confirm the cut
273                if self.ObjectMode or self.ProfileMode:
274                    # If array, remove double with intersect meshes
275                    if ((self.nbcol + self.nbrow) > 3):
276                        # Go in edit mode mode
277                        bpy.ops.object.mode_set(mode='EDIT')
278                        # Remove duplicate vertices
279                        bpy.ops.mesh.remove_doubles()
280                        # Return in object mode
281                        bpy.ops.object.mode_set(mode='OBJECT')
282
283                    if self.alt:
284                        # Save selected objects
285                        self.all_sel_obj_list = context.selected_objects.copy()
286                        if len(context.selected_objects) > 0:
287                            bpy.ops.object.select_all(action='TOGGLE')
288
289                        if self.ObjectMode:
290                            SelectObject(self, self.ObjectBrush)
291                        else:
292                            SelectObject(self, self.ProfileBrush)
293                        duplicateObject(self)
294                    else:
295                        # Brush Cut
296                        self.Cut()
297                        # Save selected objects
298                        if self.ObjectMode:
299                            if len(self.ObjectBrush.children) > 0:
300                                self.all_sel_obj_list = context.selected_objects.copy()
301                                if len(context.selected_objects) > 0:
302                                    bpy.ops.object.select_all(action='TOGGLE')
303
304                                if self.ObjectMode:
305                                    SelectObject(self, self.ObjectBrush)
306                                else:
307                                    SelectObject(self, self.ProfileBrush)
308                                duplicateObject(self)
309
310                        UndoListUpdate(self)
311
312                    # Save cursor position
313                    self.SavMousePos = self.CurLoc
314                else:
315                    if self.CutMode is False:
316                        # Cut Mode
317                        self.CutType += 1
318                        if self.CutType > 2:
319                            self.CutType = 0
320                    else:
321                        if self.CutType == self.line:
322                            # Cuts creation
323                            CreateCutLine(self, context)
324                            if self.CreateMode:
325                                # Object creation
326                                self.CreateGeometry()
327                                bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
328                                # Cursor Snap
329                                context.scene.mesh_carver.DepthCursor = self.snapCursor
330                                # Object Instantiate
331                                context.scene.mesh_carver.OInstanciate = self.Instantiate
332                                # Random rotation
333                                context.scene.mesh_carver.ORandom = self.RandomRotation
334
335                                return {'FINISHED'}
336                            else:
337                                self.Cut()
338                                UndoListUpdate(self)
339
340
341#-----------------------------------------------------
342# Object creation
343#-----------------------------------------------------
344
345
346            # Object creation
347            if event.type == self.carver_prefs.Key_Create and event.value == 'PRESS':
348                if self.ExclusiveCreateMode is False:
349                    self.CreateMode = not self.CreateMode
350
351            # Auto Bevel Update
352            if event.type == self.carver_prefs.Key_Update and event.value == 'PRESS':
353                self.Auto_BevelUpdate = not self.Auto_BevelUpdate
354
355            # Boolean operation type
356            if event.type == self.carver_prefs.Key_Bool and event.value == 'PRESS':
357                if (self.ProfileMode is True) or (self.ObjectMode is True):
358                    if self.BoolOps == self.difference:
359                        self.BoolOps = self.union
360                    else:
361                        self.BoolOps = self.difference
362
363            # Brush Mode
364            if event.type == self.carver_prefs.Key_Brush and event.value == 'PRESS':
365                self.dont_apply_boolean = False
366                if (self.ProfileMode is False) and (self.ObjectMode is False):
367                    self.ProfileMode = True
368                else:
369                    self.ProfileMode = False
370                    if self.ObjectBrush is not None:
371                        if self.ObjectMode is False:
372                            self.ObjectMode = True
373                            self.BrushSolidify = False
374                            self.CList = self.OB_List
375
376                            Selection_Save_Restore(self)
377                            context.scene.mesh_carver.nProfile = self.nProfil
378                        else:
379                            self.ObjectMode = False
380                    else:
381                        self.BrushSolidify = False
382                        Selection_Save_Restore(self)
383
384                if self.ProfileMode:
385                    createMeshFromData(self)
386                    self.ProfileBrush = bpy.data.objects["CT_Profil"]
387                    Selection_Save(self)
388                    self.BrushSolidify = True
389
390                    bpy.ops.object.select_all(action='TOGGLE')
391                    self.ProfileBrush.select_set(True)
392                    context.view_layer.objects.active = self.ProfileBrush
393                    # Set xRay
394                    self.ProfileBrush.show_in_front = True
395
396                    bpy.ops.object.modifier_add(type='SOLIDIFY')
397                    context.object.modifiers["Solidify"].name = "CT_SOLIDIFY"
398                    context.object.modifiers["CT_SOLIDIFY"].thickness = 0.1
399
400                    Selection_Restore(self)
401
402                    self.CList = self.CurrentSelection
403                else:
404                    if self.ObjectBrush is not None:
405                        if self.ObjectMode is False:
406                            if self.ObjectBrush is not None:
407                                self.ObjectBrush.location = self.InitBrush['location']
408                                self.ObjectBrush.scale = self.InitBrush['scale']
409                                self.ObjectBrush.rotation_quaternion = self.InitBrush['rotation_quaternion']
410                                self.ObjectBrush.rotation_euler = self.InitBrush['rotation_euler']
411                                self.ObjectBrush.display_type = self.InitBrush['display_type']
412                                self.ObjectBrush.show_in_front = self.InitBrush['show_in_front']
413
414                                #Store active and selected objects
415                                Selection_Save(self)
416
417                                #Remove Carver modifier
418                                self.BrushSolidify = False
419                                bpy.ops.object.select_all(action='TOGGLE')
420                                self.ObjectBrush.select_set(True)
421                                context.view_layer.objects.active = self.ObjectBrush
422                                bpy.ops.object.modifier_remove(modifier="CT_SOLIDIFY")
423
424                                #Restore selected and active object
425                                Selection_Restore(self)
426                        else:
427                            if self.SolidifyPossible:
428                                #Store active and selected objects
429                                Selection_Save(self)
430                                self.BrushSolidify = True
431                                bpy.ops.object.select_all(action='TOGGLE')
432                                self.ObjectBrush.select_set(True)
433                                context.view_layer.objects.active = self.ObjectBrush
434                                # Set xRay
435                                self.ObjectBrush.show_in_front = True
436                                bpy.ops.object.modifier_add(type='SOLIDIFY')
437                                context.object.modifiers["Solidify"].name = "CT_SOLIDIFY"
438                                context.object.modifiers["CT_SOLIDIFY"].thickness = 0.1
439
440                                #Restore selected and active object
441                                Selection_Restore(self)
442
443            # Help display
444            if event.type == self.carver_prefs.Key_Help and event.value == 'PRESS':
445                self.AskHelp = not self.AskHelp
446
447            # Instantiate object
448            if event.type == self.carver_prefs.Key_Instant and event.value == 'PRESS':
449                self.Instantiate = not self.Instantiate
450
451            # Close polygonal shape
452            if event.type == self.carver_prefs.Key_Close and event.value == 'PRESS':
453                if self.CreateMode:
454                    self.Closed = not self.Closed
455
456            if event.type == self.carver_prefs.Key_Apply and event.value == 'PRESS':
457                self.dont_apply_boolean = not self.dont_apply_boolean
458
459            # Scale object
460            if event.type == self.carver_prefs.Key_Scale and event.value == 'PRESS':
461                if self.ObjectScale is False:
462                    self.mouse_region = event.mouse_region_x, event.mouse_region_y
463                self.ObjectScale = True
464
465            # Grid : Snap on grid
466            if event.type == self.carver_prefs.Key_Snap and event.value == 'PRESS':
467                self.snap = not self.snap
468
469            # Array : Add column
470            if event.type == 'UP_ARROW' and event.value == 'PRESS':
471                self.nbrow += 1
472                update_grid(self, context)
473
474            # Array : Delete column
475            elif event.type == 'DOWN_ARROW' and event.value == 'PRESS':
476                self.nbrow -= 1
477                update_grid(self, context)
478
479            # Array : Add row
480            elif event.type == 'RIGHT_ARROW' and event.value == 'PRESS':
481                self.nbcol += 1
482                update_grid(self, context)
483
484            # Array : Delete row
485            elif event.type == 'LEFT_ARROW' and event.value == 'PRESS':
486                self.nbcol -= 1
487                update_grid(self, context)
488
489            # Array : Scale gap between columns
490            if event.type == self.carver_prefs.Key_Gapy and event.value == 'PRESS':
491                if self.GridScaleX is False:
492                    self.mouse_region = event.mouse_region_x, event.mouse_region_y
493                self.GridScaleX = True
494
495            # Array : Scale gap between rows
496            if event.type == self.carver_prefs.Key_Gapx and event.value == 'PRESS':
497                if self.GridScaleY is False:
498                    self.mouse_region = event.mouse_region_x, event.mouse_region_y
499                self.GridScaleY = True
500
501            # Cursor depth or solidify pattern
502            if event.type == self.carver_prefs.Key_Depth and event.value == 'PRESS':
503                if (self.ObjectMode is False) and (self.ProfileMode is False):
504                    self.snapCursor = not self.snapCursor
505                else:
506                    # Solidify
507
508                    if (self.ObjectMode or self.ProfileMode) and (self.SolidifyPossible):
509                        solidify = True
510
511                        if self.ObjectMode:
512                            z = self.ObjectBrush.data.vertices[0].co.z
513                            ErrorMarge = 0.01
514                            for v in self.ObjectBrush.data.vertices:
515                                if abs(v.co.z - z) > ErrorMarge:
516                                    solidify = False
517                                    self.CarveDepth = True
518                                    self.mouse_region = event.mouse_region_x, event.mouse_region_y
519                                    break
520
521                        if solidify:
522                            if self.ObjectMode:
523                                for mb in self.ObjectBrush.modifiers:
524                                    if mb.type == 'SOLIDIFY':
525                                        AlreadySoldify = True
526                            else:
527                                for mb in self.ProfileBrush.modifiers:
528                                    if mb.type == 'SOLIDIFY':
529                                        AlreadySoldify = True
530
531                            if AlreadySoldify is False:
532                                Selection_Save(self)
533                                self.BrushSolidify = True
534
535                                bpy.ops.object.select_all(action='TOGGLE')
536                                if self.ObjectMode:
537                                    self.ObjectBrush.select_set(True)
538                                    context.view_layer.objects.active = self.ObjectBrush
539                                    # Active le xray
540                                    self.ObjectBrush.show_in_front = True
541                                else:
542                                    self.ProfileBrush.select_set(True)
543                                    context.view_layer.objects.active = self.ProfileBrush
544                                    # Active le xray
545                                    self.ProfileBrush.show_in_front = True
546
547                                bpy.ops.object.modifier_add(type='SOLIDIFY')
548                                context.object.modifiers["Solidify"].name = "CT_SOLIDIFY"
549
550                                context.object.modifiers["CT_SOLIDIFY"].thickness = 0.1
551
552                                Selection_Restore(self)
553
554                            self.WidthSolidify = not self.WidthSolidify
555                            self.mouse_region = event.mouse_region_x, event.mouse_region_y
556
557            if event.type == self.carver_prefs.Key_BrushDepth and event.value == 'PRESS':
558                if self.ObjectMode:
559                    self.CarveDepth = False
560
561                    self.BrushDepth = True
562                    self.mouse_region = event.mouse_region_x, event.mouse_region_y
563
564            # Random rotation
565            if event.type == 'R' and event.value == 'PRESS':
566                self.RandomRotation = not self.RandomRotation
567
568            # Undo
569            if event.type == 'Z' and event.value == 'PRESS':
570                if self.ctrl:
571                    if (self.CutType == self.line) and (self.CutMode):
572                        if len(self.mouse_path) > 1:
573                            self.mouse_path[len(self.mouse_path) - 1:] = []
574                    else:
575                        Undo(self)
576
577            # Mouse move
578            if event.type == 'MOUSEMOVE' :
579                if self.ObjectMode or self.ProfileMode:
580                    fac = 50.0
581                    if self.shift:
582                        fac = 500.0
583                    if self.WidthSolidify:
584                        if self.ObjectMode:
585                            bpy.data.objects[self.ObjectBrush.name].modifiers[
586                                "CT_SOLIDIFY"].thickness += (event.mouse_region_x - self.mouse_region[0]) / fac
587                        elif self.ProfileMode:
588                            bpy.data.objects[self.ProfileBrush.name].modifiers[
589                                "CT_SOLIDIFY"].thickness += (event.mouse_region_x - self.mouse_region[0]) / fac
590                        self.mouse_region = event.mouse_region_x, event.mouse_region_y
591                    elif self.CarveDepth:
592                        for v in self.ObjectBrush.data.vertices:
593                            v.co.z += (event.mouse_region_x - self.mouse_region[0]) / fac
594                        self.mouse_region = event.mouse_region_x, event.mouse_region_y
595                    elif self.BrushDepth:
596                        self.BrushDepthOffset += (event.mouse_region_x - self.mouse_region[0]) / fac
597                        self.mouse_region = event.mouse_region_x, event.mouse_region_y
598                    else:
599                        if (self.GridScaleX):
600                            self.gapx += (event.mouse_region_x - self.mouse_region[0]) / 50
601                            self.mouse_region = event.mouse_region_x, event.mouse_region_y
602                            update_grid(self, context)
603                            return {'RUNNING_MODAL'}
604
605                        elif (self.GridScaleY):
606                            self.gapy += (event.mouse_region_x - self.mouse_region[0]) / 50
607                            self.mouse_region = event.mouse_region_x, event.mouse_region_y
608                            update_grid(self, context)
609                            return {'RUNNING_MODAL'}
610
611                        elif self.ObjectScale:
612                            self.ascale = -(event.mouse_region_x - self.mouse_region[0])
613                            self.mouse_region = event.mouse_region_x, event.mouse_region_y
614
615                            if self.ObjectMode:
616                                self.ObjectBrush.scale.x -= float(self.ascale) / 150.0
617                                if self.ObjectBrush.scale.x <= 0.0:
618                                    self.ObjectBrush.scale.x = 0.0
619                                self.ObjectBrush.scale.y -= float(self.ascale) / 150.0
620                                if self.ObjectBrush.scale.y <= 0.0:
621                                    self.ObjectBrush.scale.y = 0.0
622                                self.ObjectBrush.scale.z -= float(self.ascale) / 150.0
623                                if self.ObjectBrush.scale.z <= 0.0:
624                                    self.ObjectBrush.scale.z = 0.0
625
626                            elif self.ProfileMode:
627                                if self.ProfileBrush is not None:
628                                    self.ProfileBrush.scale.x -= float(self.ascale) / 150.0
629                                    self.ProfileBrush.scale.y -= float(self.ascale) / 150.0
630                                    self.ProfileBrush.scale.z -= float(self.ascale) / 150.0
631                        else:
632                            if self.LMB:
633                                if self.ctrl:
634                                    self.aRotZ = - \
635                                        ((int((event.mouse_region_x - self.xSavMouse) / 10.0) * PI / 4.0) * 25.0)
636                                else:
637                                    self.aRotZ -= event.mouse_region_x - self.mouse_region[0]
638                                self.ascale = 0.0
639
640                                self.mouse_region = event.mouse_region_x, event.mouse_region_y
641                            else:
642                                target_hit, target_normal, target_eul_rotation = Pick(context, event, self)
643                                if target_hit is not None:
644                                    self.ShowCursor = True
645                                    up_vector = Vector((0.0, 0.0, 1.0))
646                                    quat_rot_axis = rot_axis_quat(up_vector, target_normal)
647                                    self.quat_rot = target_eul_rotation @ quat_rot_axis
648                                    MoveCursor(quat_rot_axis, target_hit, self)
649                                    self.SavCurLoc = target_hit
650                                    if self.ctrl:
651                                        if self.SavMousePos is not None:
652                                            xEcart = abs(self.SavMousePos.x - self.SavCurLoc.x)
653                                            yEcart = abs(self.SavMousePos.y - self.SavCurLoc.y)
654                                            zEcart = abs(self.SavMousePos.z - self.SavCurLoc.z)
655                                            if (xEcart > yEcart) and (xEcart > zEcart):
656                                                self.CurLoc = Vector(
657                                                    (target_hit.x, self.SavMousePos.y, self.SavMousePos.z))
658                                            if (yEcart > xEcart) and (yEcart > zEcart):
659                                                self.CurLoc = Vector(
660                                                    (self.SavMousePos.x, target_hit.y, self.SavMousePos.z))
661                                            if (zEcart > xEcart) and (zEcart > yEcart):
662                                                self.CurLoc = Vector(
663                                                    (self.SavMousePos.x, self.SavMousePos.y, target_hit.z))
664                                            else:
665                                                self.CurLoc = target_hit
666                                    else:
667                                        self.CurLoc = target_hit
668                else:
669                    if self.CutMode:
670                        if self.alt is False:
671                            if self.ctrl :
672                                # Find the closest position on the overlay grid and snap the mouse on it
673                                # Draw a mini grid around the cursor
674                                mouse_pos = [[event.mouse_region_x, event.mouse_region_y]]
675                                Snap_Cursor(self, context, event, mouse_pos)
676
677                            else:
678                                if len(self.mouse_path) > 0:
679                                    self.mouse_path[len(self.mouse_path) -
680                                                    1] = (event.mouse_region_x, event.mouse_region_y)
681                        else:
682                            # [ALT] press, update position
683                            self.xpos += (event.mouse_region_x - self.last_mouse_region_x)
684                            self.ypos += (event.mouse_region_y - self.last_mouse_region_y)
685
686                            self.last_mouse_region_x = event.mouse_region_x
687                            self.last_mouse_region_y = event.mouse_region_y
688
689            elif event.type == 'LEFTMOUSE' and event.value == 'PRESS':
690                if self.ObjectMode or self.ProfileMode:
691                    if self.LMB is False:
692                        target_hit, target_normal, target_eul_rotation  = Pick(context, event, self)
693                        if target_hit is not None:
694                            up_vector = Vector((0.0, 0.0, 1.0))
695                            self.quat_rot_axis = rot_axis_quat(up_vector, target_normal)
696                            self.quat_rot = target_eul_rotation @ self.quat_rot_axis
697                        self.mouse_region = event.mouse_region_x, event.mouse_region_y
698                        self.xSavMouse = event.mouse_region_x
699
700                        if self.ctrl:
701                            self.nRotZ = int((self.aRotZ / 25.0) / (PI / 4.0))
702                            self.aRotZ = self.nRotZ * (PI / 4.0) * 25.0
703
704                    self.LMB = True
705
706            # LEFTMOUSE
707            elif event.type == 'LEFTMOUSE' and event.value == 'RELEASE' and self.in_view_3d:
708                if self.ObjectMode or self.ProfileMode:
709                    # Rotation and scale
710                    self.LMB = False
711                    if self.ObjectScale is True:
712                        self.ObjectScale = False
713
714                    if self.GridScaleX is True:
715                        self.GridScaleX = False
716
717                    if self.GridScaleY is True:
718                        self.GridScaleY = False
719
720                    if self.WidthSolidify:
721                        self.WidthSolidify = False
722
723                    if self.CarveDepth is True:
724                        self.CarveDepth = False
725
726                    if self.BrushDepth is True:
727                        self.BrushDepth = False
728
729                else:
730                    if self.CutMode is False:
731                        if self.ctrl:
732                            Picking(context, event)
733
734                        else:
735
736                            if self.CutType == self.line:
737                                if self.CutMode is False:
738                                    self.mouse_path.clear()
739                                    self.mouse_path.append((event.mouse_region_x, event.mouse_region_y))
740                                    self.mouse_path.append((event.mouse_region_x, event.mouse_region_y))
741                            else:
742                                self.mouse_path[0] = (event.mouse_region_x, event.mouse_region_y)
743                                self.mouse_path[1] = (event.mouse_region_x, event.mouse_region_y)
744                            self.CutMode = True
745                    else:
746                        if self.CutType != self.line:
747                            # Cut creation
748                            if self.CutType == self.rectangle:
749                                CreateCutSquare(self, context)
750                            if self.CutType == self.circle:
751                                CreateCutCircle(self, context)
752                            if self.CutType == self.line:
753                                CreateCutLine(self, context)
754
755                            if self.CreateMode:
756                                # Object creation
757                                self.CreateGeometry()
758                                bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
759                                # Depth Cursor
760                                context.scene.mesh_carver.DepthCursor = self.snapCursor
761                                # Instantiate object
762                                context.scene.mesh_carver.OInstanciate = self.Instantiate
763                                # Random rotation
764                                context.scene.mesh_carver.ORandom = self.RandomRotation
765                                # Apply operation
766                                context.scene.mesh_carver.DontApply = self.dont_apply_boolean
767
768                                # if Object mode, set initiale state
769                                if self.ObjectBrush is not None:
770                                    self.ObjectBrush.location = self.InitBrush['location']
771                                    self.ObjectBrush.scale = self.InitBrush['scale']
772                                    self.ObjectBrush.rotation_quaternion = self.InitBrush['rotation_quaternion']
773                                    self.ObjectBrush.rotation_euler = self.InitBrush['rotation_euler']
774                                    self.ObjectBrush.display_type = self.InitBrush['display_type']
775                                    self.ObjectBrush.show_in_front = self.InitBrush['show_in_front']
776
777                                    # remove solidify
778                                    Selection_Save(self)
779                                    self.BrushSolidify = False
780
781                                    bpy.ops.object.select_all(action='TOGGLE')
782                                    self.ObjectBrush.select_set(True)
783                                    context.view_layer.objects.active = self.ObjectBrush
784
785                                    bpy.ops.object.modifier_remove(modifier="CT_SOLIDIFY")
786
787                                    Selection_Restore(self)
788
789                                    context.scene.mesh_carver.nProfile = self.nProfil
790
791                                return {'FINISHED'}
792                            else:
793                                self.Cut()
794                                UndoListUpdate(self)
795                        else:
796                            # Line
797                            self.mouse_path.append((event.mouse_region_x, event.mouse_region_y))
798
799            # Change brush profil or circle subdivisions
800            elif (event.type == 'COMMA' and event.value == 'PRESS') or \
801                        (event.type == self.carver_prefs.Key_Subrem and event.value == 'PRESS'):
802                # Brush profil
803                if self.ProfileMode:
804                    self.nProfil += 1
805                    if self.nProfil >= self.MaxProfil:
806                        self.nProfil = 0
807                    createMeshFromData(self)
808                # Circle subdivisions
809                if self.CutType == self.circle:
810                    self.step += 1
811                    if self.step >= len(self.stepAngle):
812                        self.step = len(self.stepAngle) - 1
813            # Change brush profil or circle subdivisions
814            elif (event.type == 'PERIOD' and event.value == 'PRESS') or \
815                        (event.type == self.carver_prefs.Key_Subadd and event.value == 'PRESS'):
816                # Brush profil
817                if self.ProfileMode:
818                    self.nProfil -= 1
819                    if self.nProfil < 0:
820                        self.nProfil = self.MaxProfil - 1
821                    createMeshFromData(self)
822                # Circle subdivisions
823                if self.CutType == self.circle:
824                    if self.step > 0:
825                        self.step -= 1
826            # Quit
827            elif event.type in {'RIGHTMOUSE', 'ESC'}:
828                # Depth Cursor
829                context.scene.mesh_carver.DepthCursor = self.snapCursor
830                # Instantiate object
831                context.scene.mesh_carver.OInstanciate = self.Instantiate
832                # Random Rotation
833                context.scene.mesh_carver.ORandom = self.RandomRotation
834                # Apply boolean operation
835                context.scene.mesh_carver.DontApply = self.dont_apply_boolean
836
837                # Reset Object
838                if self.ObjectBrush is not None:
839                    self.ObjectBrush.location = self.InitBrush['location']
840                    self.ObjectBrush.scale = self.InitBrush['scale']
841                    self.ObjectBrush.rotation_quaternion = self.InitBrush['rotation_quaternion']
842                    self.ObjectBrush.rotation_euler = self.InitBrush['rotation_euler']
843                    self.ObjectBrush.display_type = self.InitBrush['display_type']
844                    self.ObjectBrush.show_in_front = self.InitBrush['show_in_front']
845
846                    # Remove solidify modifier
847                    Selection_Save(self)
848                    self.BrushSolidify = False
849
850                    bpy.ops.object.select_all(action='TOGGLE')
851                    self.ObjectBrush.select_set(True)
852                    context.view_layer.objects.active = self.ObjectBrush
853
854                    bpy.ops.object.modifier_remove(modifier="CT_SOLIDIFY")
855                    bpy.ops.object.select_all(action='TOGGLE')
856
857                    Selection_Restore(self)
858
859                Selection_Save_Restore(self)
860                context.view_layer.objects.active = self.CurrentActive
861                context.scene.mesh_carver.nProfile = self.nProfil
862
863                bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
864
865                # Remove Copy Object Brush
866                if bpy.data.objects.get("CarverBrushCopy") is not None:
867                    brush = bpy.data.objects["CarverBrushCopy"]
868                    self.ObjectBrush.data = bpy.data.meshes[brush.data.name]
869                    bpy.ops.object.select_all(action='DESELECT')
870                    bpy.data.objects["CarverBrushCopy"].select_set(True)
871                    bpy.ops.object.delete()
872
873                return {'FINISHED'}
874
875            return {'RUNNING_MODAL'}
876
877        except:
878            print("\n[Carver MT ERROR]\n")
879            import traceback
880            traceback.print_exc()
881
882            context.window.cursor_modal_set("DEFAULT")
883            context.area.header_text_set(None)
884            bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
885
886            self.report({'WARNING'},
887                        "Operation finished. Failure during Carving (Check the console for more info)")
888
889            return {'FINISHED'}
890
891    def cancel(self, context):
892        # Note: used to prevent memory leaks on quitting Blender while the modal operator
893        # is still running, gets called on return {"CANCELLED"}
894        bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
895
896    def invoke(self, context, event):
897        if context.area.type != 'VIEW_3D':
898            self.report({'WARNING'},
899                        "View3D not found or not currently active. Operation Cancelled")
900            self.cancel(context)
901            return {'CANCELLED'}
902
903        # test if some other object types are selected that are not meshes
904        for obj in context.selected_objects:
905            if obj.type != "MESH":
906                self.report({'WARNING'},
907                "Some selected objects are not of the Mesh type. Operation Cancelled")
908                self.cancel(context)
909                return {'CANCELLED'}
910
911        if context.mode == 'EDIT_MESH':
912            bpy.ops.object.mode_set(mode='OBJECT')
913
914        #Load the Carver preferences
915        self.carver_prefs = bpy.context.preferences.addons[__package__].preferences
916
917        # Get default patterns
918        self.Profils = []
919        for p in Profils:
920            self.Profils.append((p[0], p[1], p[2], p[3]))
921
922        for o in context.scene.objects:
923            if not o.name.startswith(self.carver_prefs.ProfilePrefix):
924                continue
925            # In-scene profiles may have changed, remove them to refresh
926            for m in bpy.data.meshes:
927                if m.name.startswith(self.carver_prefs.ProfilePrefix):
928                    bpy.data.meshes.remove(m)
929
930            vertices = []
931            for v in o.data.vertices:
932                vertices.append((v.co.x, v.co.y, v.co.z))
933
934            faces = []
935            for f in o.data.polygons:
936                face = []
937                for v in f.vertices:
938                    face.append(v)
939
940                faces.append(face)
941
942            self.Profils.append(
943                (o.name,
944                Vector((o.location.x, o.location.y, o.location.z)),
945                vertices, faces)
946            )
947
948        self.nProfil = context.scene.mesh_carver.nProfile
949        self.MaxProfil = len(self.Profils)
950
951
952        # reset selected profile if last profile exceeds length of array
953        if self.nProfil >= self.MaxProfil:
954            self.nProfil = context.scene.mesh_carver.nProfile = 0
955
956        if len(context.selected_objects) > 1:
957            self.ObjectBrush = context.active_object
958
959            # Copy the brush object
960            ob = bpy.data.objects.new("CarverBrushCopy", context.object.data.copy())
961            ob.location = self.ObjectBrush.location
962            context.collection.objects.link(ob)
963            context.view_layer.update()
964
965            # Save default variables
966            self.InitBrush['location'] = self.ObjectBrush.location.copy()
967            self.InitBrush['scale'] = self.ObjectBrush.scale.copy()
968            self.InitBrush['rotation_quaternion'] = self.ObjectBrush.rotation_quaternion.copy()
969            self.InitBrush['rotation_euler'] = self.ObjectBrush.rotation_euler.copy()
970            self.InitBrush['display_type'] = self.ObjectBrush.display_type
971            self.InitBrush['show_in_front'] = self.ObjectBrush.show_in_front
972
973            # Test if flat object
974            z = self.ObjectBrush.data.vertices[0].co.z
975            ErrorMarge = 0.01
976            self.SolidifyPossible = True
977            for v in self.ObjectBrush.data.vertices:
978                if abs(v.co.z - z) > ErrorMarge:
979                    self.SolidifyPossible = False
980                    break
981
982        self.CList = []
983        self.OPList = []
984        self.RList = []
985        self.OB_List = []
986
987        for obj in context.selected_objects:
988            if obj != self.ObjectBrush:
989                self.OB_List.append(obj)
990
991        # Left button
992        self.LMB = False
993
994        # Undo Variables
995        self.undo_index = 0
996        self.undo_limit = context.preferences.edit.undo_steps
997        self.undo_list = []
998
999        # Boolean operations type
1000        self.BooleanType = 0
1001
1002        self.UList = []
1003        self.UList_Index = -1
1004        self.UndoOps = []
1005
1006        context.window_manager.modal_handler_add(self)
1007        return {'RUNNING_MODAL'}
1008
1009    #Get the region area where the operator is used
1010    def check_region(self,context,event):
1011        if context.area != None:
1012            if context.area.type == "VIEW_3D" :
1013                for region in context.area.regions:
1014                    if region.type == "TOOLS":
1015                        t_panel = region
1016                    elif region.type == "UI":
1017                        ui_panel = region
1018
1019                view_3d_region_x = Vector((context.area.x + t_panel.width, context.area.x + context.area.width - ui_panel.width))
1020                view_3d_region_y = Vector((context.region.y, context.region.y+context.region.height))
1021
1022                if (event.mouse_x > view_3d_region_x[0] and event.mouse_x < view_3d_region_x[1] \
1023                and event.mouse_y > view_3d_region_y[0] and event.mouse_y < view_3d_region_y[1]):
1024                    self.in_view_3d = True
1025                else:
1026                    self.in_view_3d = False
1027            else:
1028                self.in_view_3d = False
1029
1030    def CreateGeometry(self):
1031        context = bpy.context
1032        in_local_view = False
1033
1034        for area in context.screen.areas:
1035            if area.type == 'VIEW_3D':
1036                if area.spaces[0].local_view is not None:
1037                    in_local_view = True
1038
1039        if in_local_view:
1040            bpy.ops.view3d.localview()
1041
1042        if self.ExclusiveCreateMode:
1043            # Default width
1044            objBBDiagonal = 0.5
1045        else:
1046            ActiveObj = self.CurrentSelection[0]
1047            if ActiveObj is not None:
1048                # Object dimensions
1049                objBBDiagonal = objDiagonal(ActiveObj) / 4
1050        subdivisions = 2
1051
1052        if len(context.selected_objects) > 0:
1053            bpy.ops.object.select_all(action='TOGGLE')
1054
1055        context.view_layer.objects.active = self.CurrentObj
1056
1057        bpy.data.objects[self.CurrentObj.name].select_set(True)
1058        bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
1059
1060        bpy.ops.object.mode_set(mode='EDIT')
1061        bpy.ops.mesh.select_all(action='SELECT')
1062        bpy.ops.mesh.select_mode(type="EDGE")
1063        if self.snapCursor is False:
1064            bpy.ops.transform.translate(value=self.ViewVector * objBBDiagonal * subdivisions)
1065        bpy.ops.mesh.extrude_region_move(
1066            TRANSFORM_OT_translate={"value": -self.ViewVector * objBBDiagonal * subdivisions * 2})
1067
1068        bpy.ops.mesh.select_all(action='SELECT')
1069        bpy.ops.mesh.normals_make_consistent()
1070        bpy.ops.object.mode_set(mode='OBJECT')
1071
1072        saved_location_0 = context.scene.cursor.location.copy()
1073        bpy.ops.view3d.snap_cursor_to_active()
1074        saved_location = context.scene.cursor.location.copy()
1075        bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
1076        context.scene.cursor.location = saved_location
1077        bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
1078        context.scene.cursor.location = saved_location_0
1079
1080        bpy.data.objects[self.CurrentObj.name].select_set(True)
1081        bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
1082
1083        for o in self.all_sel_obj_list:
1084            bpy.data.objects[o.name].select_set(True)
1085
1086        if in_local_view:
1087            bpy.ops.view3d.localview()
1088
1089        self.CutMode = False
1090        self.mouse_path.clear()
1091        self.mouse_path = [(0, 0), (0, 0)]
1092
1093    def Cut(self):
1094        context = bpy.context
1095
1096        # Local view ?
1097        in_local_view = False
1098        for area in context.screen.areas:
1099            if area.type == 'VIEW_3D':
1100                if area.spaces[0].local_view is not None:
1101                    in_local_view = True
1102
1103        if in_local_view:
1104            bpy.ops.view3d.localview()
1105
1106        # Save cursor position
1107        CursorLocation = context.scene.cursor.location.copy()
1108
1109        #List of selected objects
1110        selected_obj_list = []
1111
1112        #Cut Mode with line
1113        if (self.ObjectMode is False) and (self.ProfileMode is False):
1114
1115            #Compute the bounding Box
1116            objBBDiagonal = objDiagonal(self.CurrentSelection[0])
1117            if self.dont_apply_boolean:
1118                subdivisions = 1
1119            else:
1120                subdivisions = 32
1121
1122            # Get selected objects
1123            selected_obj_list = context.selected_objects.copy()
1124
1125            bpy.ops.object.select_all(action='TOGGLE')
1126
1127            context.view_layer.objects.active = self.CurrentObj
1128
1129            bpy.data.objects[self.CurrentObj.name].select_set(True)
1130            bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
1131
1132            bpy.ops.object.mode_set(mode='EDIT')
1133            bpy.ops.mesh.select_all(action='SELECT')
1134            bpy.ops.mesh.select_mode(type="EDGE")
1135            #Translate the created mesh away from the view
1136            if (self.snapCursor is False) or (self.ForceRebool):
1137                bpy.ops.transform.translate(value=self.ViewVector * objBBDiagonal * subdivisions)
1138            #Extrude the mesh region and move the result
1139            bpy.ops.mesh.extrude_region_move(
1140                TRANSFORM_OT_translate={"value": -self.ViewVector * objBBDiagonal * subdivisions * 2})
1141            bpy.ops.mesh.select_all(action='SELECT')
1142            bpy.ops.mesh.normals_make_consistent()
1143            bpy.ops.object.mode_set(mode='OBJECT')
1144        else:
1145            # Create list
1146            if self.ObjectMode:
1147                for o in self.CurrentSelection:
1148                    if o != self.ObjectBrush:
1149                        selected_obj_list.append(o)
1150                self.CurrentObj = self.ObjectBrush
1151            else:
1152                selected_obj_list = self.CurrentSelection
1153                self.CurrentObj = self.ProfileBrush
1154
1155        for obj in self.CurrentSelection:
1156            UndoAdd(self, "MESH", obj)
1157
1158        # List objects create with rebool
1159        lastSelected = []
1160
1161        for ActiveObj in selected_obj_list:
1162            context.scene.cursor.location = CursorLocation
1163
1164            if len(context.selected_objects) > 0:
1165                bpy.ops.object.select_all(action='TOGGLE')
1166
1167            # Select cut object
1168            bpy.data.objects[self.CurrentObj.name].select_set(True)
1169            context.view_layer.objects.active = self.CurrentObj
1170
1171            bpy.ops.object.mode_set(mode='EDIT')
1172            bpy.ops.mesh.select_all(action='SELECT')
1173            bpy.ops.object.mode_set(mode='OBJECT')
1174
1175            # Select object to cut
1176            bpy.data.objects[ActiveObj.name].select_set(True)
1177            context.view_layer.objects.active = ActiveObj
1178
1179            bpy.ops.object.mode_set(mode='EDIT')
1180            bpy.ops.mesh.select_all(action='DESELECT')
1181            bpy.ops.object.mode_set(mode='OBJECT')
1182
1183            # Boolean operation
1184            if (self.shift is False) and (self.ForceRebool is False):
1185                if self.ObjectMode or self.ProfileMode:
1186                    if self.BoolOps == self.union:
1187                        boolean_operation(bool_type="UNION")
1188                    else:
1189                        boolean_operation(bool_type="DIFFERENCE")
1190                else:
1191                    boolean_operation(bool_type="DIFFERENCE")
1192
1193                # Apply booleans
1194                if self.dont_apply_boolean is False:
1195                    BMname = "CT_" + self.CurrentObj.name
1196                    for mb in ActiveObj.modifiers:
1197                        if (mb.type == 'BOOLEAN') and (mb.name == BMname):
1198                            try:
1199                                bpy.ops.object.modifier_apply(modifier=BMname)
1200                            except:
1201                                bpy.ops.object.modifier_remove(modifier=BMname)
1202                                exc_type, exc_value, exc_traceback = sys.exc_info()
1203                                self.report({'ERROR'}, str(exc_value))
1204
1205                bpy.ops.object.select_all(action='TOGGLE')
1206            else:
1207                if self.ObjectMode or self.ProfileMode:
1208                    for mb in self.CurrentObj.modifiers:
1209                        if (mb.type == 'SOLIDIFY') and (mb.name == "CT_SOLIDIFY"):
1210                            try:
1211                                bpy.ops.object.modifier_apply(modifier="CT_SOLIDIFY")
1212                            except:
1213                                exc_type, exc_value, exc_traceback = sys.exc_info()
1214                                self.report({'ERROR'}, str(exc_value))
1215
1216                # Rebool
1217                Rebool(context, self)
1218
1219                # Test if not empty object
1220                if context.selected_objects[0]:
1221                    rebool_RT = context.selected_objects[0]
1222                    if len(rebool_RT.data.vertices) > 0:
1223                        # Create Bevel for new objects
1224                        CreateBevel(context, context.selected_objects[0])
1225
1226                        UndoAdd(self, "REBOOL", context.selected_objects[0])
1227
1228                        context.scene.cursor.location = ActiveObj.location
1229                        bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
1230                    else:
1231                        bpy.ops.object.delete(use_global=False)
1232
1233                context.scene.cursor.location = CursorLocation
1234
1235                if self.ObjectMode:
1236                    context.view_layer.objects.active = self.ObjectBrush
1237                if self.ProfileMode:
1238                    context.view_layer.objects.active = self.ProfileBrush
1239
1240            if self.dont_apply_boolean is False:
1241                # Apply booleans
1242                BMname = "CT_" + self.CurrentObj.name
1243                for mb in ActiveObj.modifiers:
1244                    if (mb.type == 'BOOLEAN') and (mb.name == BMname):
1245                        try:
1246                            bpy.ops.object.modifier_apply(modifier=BMname)
1247                        except:
1248                            bpy.ops.object.modifier_remove(modifier=BMname)
1249                            exc_type, exc_value, exc_traceback = sys.exc_info()
1250                            self.report({'ERROR'}, str(exc_value))
1251                # Get new objects created with rebool operations
1252                if len(context.selected_objects) > 0:
1253                    if self.shift is True:
1254                        # Get the last object selected
1255                        lastSelected.append(context.selected_objects[0])
1256
1257        context.scene.cursor.location = CursorLocation
1258
1259        if self.dont_apply_boolean is False:
1260            # Remove cut object
1261            if (self.ObjectMode is False) and (self.ProfileMode is False):
1262                if len(context.selected_objects) > 0:
1263                    bpy.ops.object.select_all(action='TOGGLE')
1264                bpy.data.objects[self.CurrentObj.name].select_set(True)
1265                bpy.ops.object.delete(use_global=False)
1266            else:
1267                if self.ObjectMode:
1268                    self.ObjectBrush.display_type = self.InitBrush['display_type']
1269
1270        if len(context.selected_objects) > 0:
1271            bpy.ops.object.select_all(action='TOGGLE')
1272
1273        # Select cut objects
1274        for obj in lastSelected:
1275            bpy.data.objects[obj.name].select_set(True)
1276
1277        for ActiveObj in selected_obj_list:
1278            bpy.data.objects[ActiveObj.name].select_set(True)
1279            context.view_layer.objects.active = ActiveObj
1280        # Update bevel
1281        list_act_obj = context.selected_objects.copy()
1282        if self.Auto_BevelUpdate:
1283            update_bevel(context)
1284
1285        # Re-select initial objects
1286        bpy.ops.object.select_all(action='TOGGLE')
1287        if self.ObjectMode:
1288            # Re-select brush
1289            self.ObjectBrush.select_set(True)
1290        for ActiveObj in selected_obj_list:
1291            bpy.data.objects[ActiveObj.name].select_set(True)
1292            context.view_layer.objects.active = ActiveObj
1293
1294        # If object has children, set "Wire" draw type
1295        if self.ObjectBrush is not None:
1296            if len(self.ObjectBrush.children) > 0:
1297                self.ObjectBrush.display_type = "WIRE"
1298        if self.ProfileMode:
1299            self.ProfileBrush.display_type = "WIRE"
1300
1301        if in_local_view:
1302            bpy.ops.view3d.localview()
1303
1304        # Reset variables
1305        self.CutMode = False
1306        self.mouse_path.clear()
1307        self.mouse_path = [(0, 0), (0, 0)]
1308
1309        self.ForceRebool = False
1310
1311        # bpy.ops.mesh.customdata_custom_splitnormals_clear()
1312
1313
1314class CarverProperties(bpy.types.PropertyGroup):
1315    DepthCursor: BoolProperty(
1316        name="DepthCursor",
1317        default=False
1318    )
1319    OInstanciate: BoolProperty(
1320        name="Obj_Instantiate",
1321        default=False
1322    )
1323    ORandom: BoolProperty(
1324        name="Random_Rotation",
1325        default=False
1326    )
1327    DontApply: BoolProperty(
1328        name="Dont_Apply",
1329        default=False
1330    )
1331    nProfile: IntProperty(
1332        name="Num_Profile",
1333        default=0
1334    )
1335
1336
1337def register():
1338    from bpy.utils import register_class
1339    bpy.utils.register_class(CARVER_OT_operator)
1340    bpy.utils.register_class(CarverProperties)
1341    bpy.types.Scene.mesh_carver = bpy.props.PointerProperty(type=CarverProperties)
1342
1343def unregister():
1344    from bpy.utils import unregister_class
1345    bpy.utils.unregister_class(CarverProperties)
1346    bpy.utils.unregister_class(CARVER_OT_operator)
1347    del bpy.types.Scene.mesh_carver
1348