addons-contrib: more view_layer syntax updates
[blender-addons-contrib.git] / mesh_ktools.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19
20
21
22 bl_info = {
23         'name': "Kjartans Scripts",
24         'author': "Kjartan Tysdal",
25         'location': '"Shift+Q" and also in EditMode "W-Specials/ KTools"',
26         'description': "Adds my personal collection of small handy scripts (mostly modeling tools)",
27         'category': "Mesh",
28         'blender': (2, 76, 0),
29         'version': (0, 2, 8),
30         'wiki_url': 'http://www.kjartantysdal.com/scripts',
31 }
32
33
34 import bpy, bmesh
35 from bpy.props import (
36         StringProperty,
37         IntProperty,
38         FloatProperty,
39         EnumProperty,
40         BoolProperty,
41         BoolVectorProperty,
42         FloatVectorProperty,
43         )
44
45
46 def testPrint():
47
48     print('Hello')
49
50
51 def checkScale(): # check if scale is 0 on any of the axis, if it is then set it to 0.01
52
53     y = -1
54     for x in bpy.context.object.scale:
55         y += 1
56         if x == 0.0:
57             bpy.context.object.scale[y] = 0.01
58
59
60 #Adds "Lattice to Selection" to the Addon
61 class lattice_to_selection(bpy.types.Operator):
62         """Add a lattice deformer to the selection"""
63         bl_idname = "object.lattice_to_selection"
64         bl_label = "Lattice to Selection"
65         bl_options = {'REGISTER', 'UNDO'}
66
67         apply_rot: BoolProperty(
68                         name = "Local",
69                         description = "Orient the lattice to the active object",
70                         default = True
71                         )
72         parent_to: BoolProperty(
73                         name = "Parent to Lattice",
74                         description = "Parents all the objects to the Lattice",
75                         default = False
76                         )
77         move_first: BoolProperty(name = "First in Modifier Stack", description = "Moves the lattice modifier to be first in the stack", default = False)
78         interpolation: bpy.props.EnumProperty(
79                                    items= (('KEY_LINEAR', 'Linear', 'Linear Interpolation'),
80                                    ('KEY_CARDINAL', 'Cardinal', 'Cardinal Interpolation'),
81                                    ('KEY_CATMULL_ROM', 'Catmull Rom', 'Catmull Rom Interpolation'),
82                                    ('KEY_BSPLINE', 'BSpline', 'BSpline Interpolation')),
83                                    name = "Interpolation", default = 'KEY_BSPLINE')
84         seg_u: IntProperty( name = "Lattice U", default = 2, soft_min = 2)
85         seg_v: IntProperty( name = "Lattice V", default = 2, soft_min = 2 )
86         seg_w: IntProperty( name = "Lattice W", default = 2, soft_min = 2 )
87
88         def execute(self, context):
89
90                 apply_rot = not self.apply_rot # Global vs Local
91                 parent_to = self.parent_to # Parents all the objects to the Lattice
92                 move_first = self.move_first # moves the lattice modifier to be first in the stack
93                 interpolation = self.interpolation
94
95                 # check if there exists an active object
96                 if bpy.context.view_layer.objects.active:
97                     active_obj = bpy.context.view_layer.objects.active.name
98                 else:
99                     for x in bpy.context.selected_objects:
100                         if bpy.data.objects[x.name].type == 'MESH':
101                             bpy.context.view_layer.objects.active = bpy.data.objects[x.name]
102                             active_obj = bpy.context.view_layer.objects.active.name
103                             break
104
105
106
107                 if bpy.data.objects[active_obj].type != 'MESH':
108                     self.report({'ERROR'}, "Make sure the active object is a Mesh")
109                     return {'CANCELLED'}
110
111                 mode = bpy.context.active_object.mode
112
113
114                 if mode == 'OBJECT':
115
116
117                     # check if object type is not MESH and then deselect it
118                     for x in bpy.context.selected_objects:
119                         if bpy.data.objects[x.name].type != 'MESH':
120                             bpy.data.objects[x.name].select_set(False)
121
122
123                     org_objs = bpy.context.selected_objects
124
125
126                     bpy.ops.object.duplicate()
127
128                     # remove any modifiers
129                     if bpy.context.object.modifiers:
130                         for x in bpy.context.object.modifiers:
131                             bpy.ops.object.modifier_remove(modifier=x.name)
132
133                     if len(bpy.context.selected_objects) > 1:
134                         bpy.ops.object.join()
135
136                     # create tmp:object and store its location, rotation and dimensions
137                     bpy.ops.object.transform_apply(location=False, rotation=apply_rot, scale=True)
138                     bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='BOUNDS')
139
140                     lattice_loc = bpy.context.object.location
141                     lattice_rot = bpy.context.object.rotation_euler
142                     bbox_size = bpy.context.object.dimensions
143                     tmp_obj = bpy.context.object.name
144
145                     # create the lattice object with the lattice_loc and rot
146                     bpy.ops.object.add(radius=1, type='LATTICE', view_align=False, enter_editmode=False, location=lattice_loc, rotation=lattice_rot)
147
148                     lattice_obj = bpy.context.object
149
150                     # set dimensions / bounding box size
151                     bpy.context.object.scale = bbox_size
152
153                     bpy.ops.object.select_all(action='DESELECT')
154
155                     # select and delete the tmp_object
156                     bpy.data.objects[tmp_obj].select_set(True)
157                     bpy.ops.object.delete(use_global=False)
158
159                     # select all the original objects and assign the lattice deformer
160                     for i in org_objs:
161                        if bpy.data.objects[i.name].type == 'MESH' :
162                            bpy.context.view_layer.objects.active = bpy.data.objects[i.name]
163                            bpy.data.objects[i.name].select_set(True)
164
165                            bpy.ops.object.modifier_add(type='LATTICE')
166                            lattice_name = bpy.context.object.modifiers[len(bpy.context.object.modifiers)-1].name
167                            bpy.context.object.modifiers[lattice_name].object = lattice_obj
168                            if move_first == True:
169                                for x in bpy.context.object.modifiers:
170                                    bpy.ops.object.modifier_move_up(modifier=lattice_name)
171                        else:
172                            bpy.data.objects[i.name].select_set(True)
173
174
175                     if parent_to:
176
177                         bpy.data.objects[lattice_obj.name].select_set(True)
178                         bpy.context.view_layer.objects.active = bpy.data.objects[lattice_obj.name]
179
180                         bpy.ops.object.parent_set(type='OBJECT', keep_transform=True)
181                     else:
182
183                         bpy.ops.object.select_all(action='DESELECT')
184                         bpy.data.objects[lattice_obj.name].select_set(True)
185                         bpy.context.view_layer.objects.active = bpy.data.objects[lattice_obj.name]
186
187
188                     bpy.context.object.data.interpolation_type_u = interpolation
189                     bpy.context.object.data.interpolation_type_v = interpolation
190                     bpy.context.object.data.interpolation_type_w = interpolation
191
192                     bpy.context.object.data.points_u = self.seg_u
193                     bpy.context.object.data.points_v = self.seg_v
194                     bpy.context.object.data.points_w = self.seg_w
195
196                     checkScale()
197
198
199                 elif mode == 'EDIT':
200
201
202
203                     org_objs = bpy.context.selected_objects
204
205                     # Add vertex group and store its name in a variable
206                     bpy.ops.object.vertex_group_assign_new()
207                     v_id = len(bpy.context.object.vertex_groups)-1
208                     bpy.context.object.vertex_groups[v_id].name = 'tmp_lattice_to_selection'
209                     v_group = bpy.context.object.vertex_groups[v_id].name
210
211
212                     bpy.ops.mesh.duplicate()
213                     bpy.ops.mesh.separate(type='SELECTED')
214
215                     bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
216
217                     for x in bpy.context.selected_objects:
218                         if x not in org_objs:
219                             tmp_obj = x.name
220                             print(tmp_obj)
221
222                     bpy.ops.object.select_all(action='DESELECT')
223
224                     bpy.context.view_layer.objects.active = bpy.data.objects[tmp_obj]
225                     bpy.data.objects[tmp_obj].select_set(True)
226
227
228                     if bpy.context.object.modifiers:
229                         for x in bpy.context.object.modifiers:
230                             bpy.ops.object.modifier_remove(modifier=x.name)
231
232
233                     bpy.ops.object.transform_apply(location=False, rotation=apply_rot, scale=True)
234                     bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='BOUNDS')
235
236                     lattice_loc = bpy.context.object.location
237                     lattice_rot = bpy.context.object.rotation_euler
238                     bbox_size = bpy.context.object.dimensions
239                     tmp_obj = bpy.context.object.name
240
241
242                     bpy.ops.object.add(radius=1, type='LATTICE', view_align=False, enter_editmode=False, location=lattice_loc, rotation=lattice_rot)
243
244                     lattice_obj = bpy.context.object
245
246                     bpy.context.object.scale = bbox_size
247
248                     bpy.ops.object.select_all(action='DESELECT')
249
250
251                     bpy.data.objects[tmp_obj].select_set(True)
252
253                     bpy.ops.object.delete(use_global=False)
254
255                     bpy.context.view_layer.objects.active = bpy.data.objects[active_obj]
256                     bpy.data.objects[active_obj].select_set(True)
257
258                     bpy.ops.object.modifier_add(type='LATTICE')
259                     lattice_name = bpy.context.object.modifiers[len(bpy.context.object.modifiers)-1].name
260                     bpy.context.object.modifiers[lattice_name].object = lattice_obj
261                     bpy.context.object.modifiers[lattice_name].vertex_group = v_group
262
263                     if move_first == True:
264                         for x in bpy.context.object.modifiers:
265                             bpy.ops.object.modifier_move_up(modifier=lattice_name)
266
267
268                     bpy.ops.object.select_all(action='DESELECT')
269
270                     bpy.data.objects[lattice_obj.name].select_set(True)
271                     bpy.context.view_layer.objects.active = bpy.data.objects[lattice_obj.name]
272
273                     bpy.context.object.data.interpolation_type_u = interpolation
274                     bpy.context.object.data.interpolation_type_v = interpolation
275                     bpy.context.object.data.interpolation_type_w = interpolation
276
277                     bpy.context.object.data.points_u = self.seg_u
278                     bpy.context.object.data.points_v = self.seg_v
279                     bpy.context.object.data.points_w = self.seg_w
280
281                     checkScale()
282
283
284
285
286                 return {'FINISHED'}
287
288         def invoke( self, context, event ):
289             wm = context.window_manager
290             return wm.invoke_props_dialog( self )
291
292
293
294 #Adds Calculate Normals and Smooth to the Addon
295 class calc_normals(bpy.types.Operator):
296         """Calculates and smooths normals."""
297         bl_idname = "mesh.calc_normals"
298         bl_label = "Calculate Normals"
299         bl_options = {'REGISTER', 'UNDO'}
300
301         invert: BoolProperty(name = "Invert Normals", description = "Inverts the normals.", default = False)
302
303         def execute(self, context):
304
305                 invert = self.invert
306                 mode = bpy.context.active_object.mode
307
308                 if mode == 'OBJECT':
309
310                         sel = bpy.context.selected_objects
311                         active = bpy.context.view_layer.objects.active.name
312
313                         bpy.ops.object.shade_smooth()
314
315
316                         for ob in sel:
317                                 ob = ob.name
318                                 bpy.context.view_layer.objects.active = bpy.data.objects[ob]
319                                 bpy.ops.object.editmode_toggle()
320                                 bpy.ops.mesh.select_all(action='SELECT')
321                                 bpy.ops.mesh.normals_make_consistent(inside=invert)
322                                 bpy.ops.object.editmode_toggle()
323
324                         bpy.context.view_layer.objects.active = bpy.data.objects[active]
325
326                 elif mode == 'EDIT':
327                         bpy.ops.mesh.normals_make_consistent(inside=invert)
328
329
330
331                 return {'FINISHED'}
332
333
334
335 #Adds SnapToAxis to the Addon
336 class snaptoaxis(bpy.types.Operator):
337         """Snaps selected vertices to zero on the selected axis."""
338         bl_idname = "mesh.snaptoaxis"
339         bl_label = "Snap to Axis"
340         bl_options = {'REGISTER', 'UNDO'}
341
342         #worldspace = bpy.props.EnumProperty(items= (('OBJECT', 'Object Space', 'Snap to the object axis'),
343         #                                                                                         ('WORLD', 'World Space', 'Snap to the global axis')),
344         #                                                                                         name = "Object/World", default = 'OBJECT')
345
346         snap_x: BoolProperty(name = "Snap to X", description = "Snaps to zero in X. Also sets the axis for the mirror modifier if that button is turned on", default = True)
347         snap_y: BoolProperty(name = "Snap to Y", description = "Snaps to zero in Y. Also sets the axis for the mirror modifier if that button is turned on", default = False)
348         snap_z: BoolProperty(name = "Snap to Z", description = "Snaps to zero in Z. Also sets the axis for the mirror modifier if that button is turned on", default = False)
349
350         mirror_add: BoolProperty(name = "Add Mirror Modifier", description = "Adds a mirror modifer", default = False)
351
352         mirror_x: BoolProperty(name = "Mirror on X", description = "Sets the modifier to mirror on X", default = True)
353         mirror_y: BoolProperty(name = "Mirror on Y", description = "Sets the modifier to mirror on Y", default = False)
354         mirror_z: BoolProperty(name = "Mirror on Z", description = "Sets the modifier to mirror on Z", default = False)
355         clipping: BoolProperty(name = "Enable Clipping", description = "Prevents vertices from going through the mirror during transform", default = True)
356
357
358
359
360         def draw(self, context):
361             layout = self.layout
362             col = layout.column()
363
364             col_move = col.column(align=True)
365             row = col_move.row(align=True)
366             if self.snap_x:
367                 row.prop(self, "snap_x", text = "X", icon='CHECKBOX_HLT')
368             else:
369                 row.prop(self, "snap_x", text = "X", icon='CHECKBOX_DEHLT')
370             if self.snap_y:
371                 row.prop(self, "snap_y", text = "Y", icon='CHECKBOX_HLT')
372             else:
373                 row.prop(self, "snap_y", text = "Y", icon='CHECKBOX_DEHLT')
374             if self.snap_z:
375                 row.prop(self, "snap_z", text = "Z", icon='CHECKBOX_HLT')
376             else:
377                 row.prop(self, "snap_z", text = "Z", icon='CHECKBOX_DEHLT')
378
379             col.separator()
380
381             col_move = col.column(align=True)
382             col_move.prop(self, "mirror_add", icon = 'MODIFIER')
383             row = col_move.row(align=True)
384
385             row = col_move.row(align=True)
386             row.active = self.mirror_add
387             if self.mirror_x:
388                 row.prop(self, "mirror_x", text = "X", icon='CHECKBOX_HLT')
389             else:
390                 row.prop(self, "mirror_x", text = "X", icon='CHECKBOX_DEHLT')
391             if self.mirror_y:
392                 row.prop(self, "mirror_y", text = "Y", icon='CHECKBOX_HLT')
393             else:
394                 row.prop(self, "mirror_y", text = "Y", icon='CHECKBOX_DEHLT')
395             if self.mirror_z:
396                 row.prop(self, "mirror_z", text = "Z", icon='CHECKBOX_HLT')
397             else:
398                 row.prop(self, "mirror_z", text = "Z", icon='CHECKBOX_DEHLT')
399
400             col = col.column()
401             col.active = self.mirror_add
402             col.prop(self, "clipping")
403
404
405         def execute(self, context):
406
407                 mode = bpy.context.active_object.mode
408                 mirror_find = bpy.context.object.modifiers.find('Mirror')
409                 run = True
410
411
412                 if mode == 'EDIT':
413                     loc = bpy.context.object.location
414
415
416                     me = bpy.context.object.data
417                     bm = bmesh.from_edit_mesh(me)
418
419                     for v in bm.verts:
420                             if v.select:
421                                 if self.snap_x == True:
422                                     v.co.x = 0
423                                 if self.snap_y == True:
424                                     v.co.y = 0
425                                 if self.snap_z == True:
426                                     v.co.z = 0
427
428                     bmesh.update_edit_mesh(me, True, False)
429
430                 if self.mirror_add == True:
431
432                     if mirror_find <= -1:
433                         bpy.ops.object.modifier_add(type='MIRROR')
434
435                         bpy.context.object.modifiers['Mirror'].show_viewport = True
436
437                         run = False
438
439                     bpy.context.object.modifiers["Mirror"].use_clip = self.clipping
440                     bpy.context.object.modifiers["Mirror"].use_x = self.mirror_x
441                     bpy.context.object.modifiers["Mirror"].use_y = self.mirror_y
442                     bpy.context.object.modifiers["Mirror"].use_z = self.mirror_z
443
444
445
446
447                 return {'FINISHED'}
448
449
450
451 #Adds QuickBool to the Addon
452 class quickbool(bpy.types.Operator):
453         """Quickly carves out the selected polygons. Works best with manifold meshes."""
454         bl_idname = "mesh.quickbool"
455         bl_label = "Quick Bool"
456         bl_options = {'REGISTER', 'UNDO'}
457
458         del_bool: BoolProperty(name="Delete BoolMesh", description="Deletes the objects used for the boolean operation.", default= True)
459         move_to: BoolProperty(name="Move to layer 10", description="Moves the objects used for the boolean operation to layer 10", default= False)
460         operation: EnumProperty(items= (('UNION', 'Union', 'Combines'),
461                                                                                                  ('INTERSECT', 'Intersect', 'Keep the part that overlaps'),
462                                                                                                  ('DIFFERENCE', 'Difference', 'Cuts out')),
463                                                                                                  name = "Operation", default = 'DIFFERENCE')
464
465         def draw(self, context):
466             layout = self.layout
467             col = layout.column()
468             col.prop(self, "del_bool")
469
470             col = col.column()
471             col.active = self.del_bool == False
472             col.prop(self, "move_to")
473
474             col = layout.column()
475             col.prop(self, "operation")
476
477
478
479
480         def execute(self, context):
481
482                 del_bool = self.del_bool
483                 move_to = self.move_to
484                 mode = bpy.context.active_object.mode
485
486
487
488                 if mode == 'EDIT':
489
490                     #Boolean From Edit mode
491                     bpy.ops.mesh.select_linked()
492                     bpy.ops.mesh.separate(type='SELECTED')
493                     bpy.ops.object.editmode_toggle()
494
495                     #get name of Original+Bool object
496                     original = bpy.context.selected_objects[1].name
497                     bool = bpy.context.selected_objects[0].name
498
499                     #perform boolean
500                     bpy.ops.object.modifier_add(type='BOOLEAN')
501                     bpy.context.object.modifiers["Boolean"].object = bpy.data.objects[bool]
502                     bpy.context.object.modifiers["Boolean"].operation = self.operation
503                     bpy.ops.object.modifier_apply(apply_as='DATA', modifier="Boolean")
504
505                     #delete Bool object
506                     bpy.ops.object.select_all(action='DESELECT')
507                     bpy.ops.object.select_pattern(pattern=bool)
508
509                     bpy.context.view_layer.objects.active = bpy.data.objects[bool]
510
511                     #Delete all geo inside Shrink_Object
512                     bpy.ops.object.mode_set(mode = 'EDIT', toggle = False)
513                     bpy.ops.mesh.select_all(action='SELECT')
514                     bpy.ops.mesh.delete(type='VERT')
515                     bpy.ops.object.mode_set(mode = 'OBJECT', toggle = False)
516
517                     bpy.ops.object.delete()
518
519                     #re-enter edit mode on Original object
520                     bpy.context.view_layer.objects.active = bpy.data.objects[original]
521                     bpy.ops.object.select_pattern(pattern=original)
522                     bpy.ops.object.editmode_toggle()
523
524
525                 else:
526
527                         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
528
529                         original = bpy.context.active_object.name
530                         bool = bpy.context.selected_objects
531
532
533                         list = []
534
535                         for x in bool:
536                                 x = x.name
537                                 if x != original:
538                                         list.append(x)
539
540                         for name in list:
541                                 #Perform Boolean
542                                 bpy.ops.object.modifier_add(type='BOOLEAN')
543                                 bpy.context.object.modifiers["Boolean"].object = bpy.data.objects[name]
544                                 bpy.context.object.modifiers["Boolean"].operation = self.operation
545                                 bpy.ops.object.modifier_apply(apply_as='DATA', modifier="Boolean")
546
547
548                                 if del_bool == True:
549
550                                         bpy.ops.object.select_all(action='DESELECT')
551                                         bpy.ops.object.select_pattern(pattern=name)
552                                         bpy.context.view_layer.objects.active = bpy.data.objects[name]
553
554                             #Delete all geo inside Shrink_Object
555                                         bpy.ops.object.mode_set(mode = 'EDIT', toggle = False)
556                                         bpy.ops.mesh.select_all(action='SELECT')
557                                         bpy.ops.mesh.delete(type='VERT')
558                                         bpy.ops.object.mode_set(mode = 'OBJECT', toggle = False)
559
560                                         bpy.ops.object.delete(use_global=False)
561                                         bpy.context.view_layer.objects.active = bpy.data.objects[original]
562                                 else:
563                                         bpy.ops.object.select_all(action='DESELECT')
564                                         bpy.ops.object.select_pattern(pattern=name)
565                                         bpy.context.view_layer.objects.active = bpy.data.objects[name]
566
567                                         bpy.context.object.display_type = 'WIRE'
568
569                                         # Move to garbage layer
570                                         if move_to == True:
571                                                 bpy.ops.object.move_to_layer(layers=(False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False))
572
573                                         bpy.context.view_layer.objects.active = bpy.data.objects[original]
574
575
576                         bpy.ops.object.mode_set(mode=mode, toggle=False)
577
578
579                 return {'FINISHED'}
580
581
582
583 #Adds Autotubes to the Addon
584 class autotubes(bpy.types.Operator):
585         """Creates a spline tube based on selected edges"""
586         bl_idname = "mesh.autotubes"
587         bl_label = "Auto Tubes"
588         bl_options = {'REGISTER', 'UNDO'}
589
590         bevel: FloatProperty(name="Tube Width", description="Change width of the tube.", default=0.1, min = 0)
591         res: IntProperty(name="Tube Resolution", description="Change resolution of the tube.", default=2, min = 0, max = 20)
592
593
594         def execute(self, context):
595
596                 mode = bpy.context.active_object.mode
597                 type = bpy.context.active_object.type
598                 bevel = self.bevel
599                 res = self.res
600
601
602                 if mode == 'EDIT' and type == 'MESH':
603                         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
604                         bpy.ops.object.duplicate()
605
606                         bpy.ops.object.mode_set(mode='EDIT', toggle=False)
607                         bpy.ops.mesh.select_all(action='INVERT')
608                         bpy.ops.mesh.delete(type='EDGE')
609
610                         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
611                         bpy.ops.object.subdivision_set(level=0)
612                         bpy.ops.object.convert(target='CURVE')
613                         bpy.context.object.data.fill_mode = 'FULL'
614                         bpy.context.object.data.bevel_depth = 0.1
615                         bpy.context.object.data.splines[0].use_smooth = True
616                         bpy.context.object.data.bevel_resolution = 2
617                         bpy.ops.object.shade_smooth()
618
619                         bpy.ops.object.mode_set(mode='EDIT', toggle=False)
620                         bpy.ops.curve.spline_type_set(type='BEZIER')
621
622                         bpy.context.object.data.bevel_depth = bevel
623                         bpy.context.object.data.bevel_resolution = res
624
625                         #bpy.ops.transform.transform(('INVOKE_DEFAULT'), mode='CURVE_SHRINKFATTEN')
626
627
628
629                 elif type == 'CURVE':
630
631                         bpy.context.object.data.bevel_depth = bevel
632                         bpy.context.object.data.bevel_resolution = res
633
634                 elif mode != 'EDIT' and type == 'MESH':
635                         self.report({'ERROR'}, "This one only works in Edit mode")
636                         return {'CANCELLED'}
637
638
639                 return {'FINISHED'}
640
641
642
643 #Adds basicRename to the Addon
644
645 class basicRename(bpy.types.Operator):
646         """Renames everything to Banana"""
647         bl_idname = "object.basic_rename"
648         bl_label = "Basic Renamer"
649         bl_options = {'REGISTER', 'UNDO'}
650
651         name: StringProperty(name="Rename", description="Rename selected objects", default="banana")
652         padding: IntProperty(name = "Number Padding", description = "Adds how many padded numbers", default = 3, min = 1, max = 8)
653         prefix:    StringProperty(name="Pre Fix", description="Adds a Prefix to the name", default="")
654         post_ob: StringProperty(name="Post Fix Object", description="Adds ending to object name", default="_MDL")
655         post_data: StringProperty(name="Post Fix Data", description="Adds ending to data name", default="_DATA")
656
657
658         def execute(self, context):
659
660
661                 # The original script
662                 obj = bpy.context.selected_objects
663                 name = self.name
664                 padding = self.padding
665                 prefix = self.prefix
666                 post_ob = self.post_ob
667                 post_data = self.post_data
668                 number = 0
669                 for item in obj:
670                         number += 1
671                         item.name = "%s%s_%s%s" %(str(prefix), str(name), str(number).zfill(padding), str(post_ob))
672                         item.data.name = "%s%s_%s%s" %(str(prefix), str(name), str(number).zfill(padding), str(post_data))
673
674
675                 return {'FINISHED'}
676
677
678
679 class cut_tool(bpy.types.Operator):
680         """Context sensitive cut tool"""
681         bl_idname = "mesh.cut_tool"
682         bl_label = "Cut Tool"
683         bl_options = {'REGISTER', 'UNDO'}
684
685         cuts: IntProperty(name="Number of Cuts", description="Change the number of cuts.", default=1, min = 1, soft_max = 10)
686         loopcut: BoolProperty(name="Insert LoopCut", description="Makes a loop cut based on the selected edges", default= False)
687         smoothness: FloatProperty(name="Smoothness", description="Change the smoothness.", default=0, min = 0, soft_max = 1)
688         quad_corners: bpy.props.EnumProperty(items= (('INNERVERT', 'Inner Vert', 'How to subdivide quad corners'),
689                                                                                                  ('PATH', 'Path', 'How to subdivide quad corners'),
690                                                                                                  ('STRAIGHT_CUT', 'Straight Cut', 'How to subdivide quad corners'),
691                                                                                                  ('FAN', 'Fan', 'How to subdivide quad corners')),
692                                                                                                  name = "Quad Corner Type", default = 'STRAIGHT_CUT')
693
694         def execute(self, context):
695
696                 quad_corners = self.quad_corners
697                 cuts = self.cuts
698                 loopcut = self.loopcut
699                 smoothness = self.smoothness
700                 mode = bpy.context.active_object.mode
701
702                 if mode == 'EDIT':
703
704                         sel_mode = bpy.context.tool_settings.mesh_select_mode[:]
705
706                                                         #Checks and stores if any Vert, Edge or Face is selected.
707                         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
708
709                         me = bpy.context.object.data
710                         bm = bmesh.new()     # create an empty BMesh
711                         bm.from_mesh(me)     # fill it in from a Mesh
712                         sel = []
713                         edge_sel = []
714                         vert_sel = []
715
716                         for v in bm.faces:
717                                 if v.select:
718                                         sel.append(v.index)
719                         for v in bm.edges:
720                                 if v.select:
721                                         edge_sel.append(v.index)
722                         for v in bm.verts:
723                                 if v.select:
724                                         vert_sel.append(v.index)
725
726
727                         bm.to_mesh(me)
728                         bpy.ops.object.mode_set(mode='EDIT', toggle=False)
729
730
731                         """
732                         if len(sel) == 0 and len(edge_sel) == 0 and len(vert_sel) == 0 :
733                                 bpy.ops.mesh.knife_tool("INVOKE_DEFAULT")
734                         """
735
736
737                         if sel_mode[2] == True and len(sel) > 1:
738
739                                 vgrp = bpy.context.object.vertex_groups.active_index
740
741
742                                 #Store the Hidden Polygons
743                                 bpy.ops.mesh.select_all(action='SELECT')
744                                 bpy.ops.object.vertex_group_assign_new()
745                                 tmp_hidden = bpy.context.object.vertex_groups.active_index
746                                 bpy.ops.mesh.select_all(action='DESELECT')
747
748                                 #Select faces to be cut
749                                 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
750                                 mesh = bpy.context.active_object.data.polygons
751
752                                 for f in sel:
753                                         mesh[f].select = True
754
755                                 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
756                                 bpy.ops.mesh.hide(unselected=True)
757                                 bpy.ops.mesh.select_all(action='SELECT')
758                                 bpy.ops.mesh.region_to_loop()
759
760                                 #Store Boundry Edges
761                                 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
762
763                                 me = bpy.context.object.data
764                                 bm = bmesh.new()
765                                 bm.from_mesh(me)
766                                 boundry_edge = []
767
768                                 for v in bm.edges:
769                                         if v.select:
770                                                 boundry_edge.append(v.index)
771
772                                 bm.to_mesh(me)
773
774                                 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
775
776                                 #Store Cut Edges
777                                 bpy.ops.mesh.select_all(action='INVERT')
778                                 bpy.ops.mesh.loop_multi_select(ring=True)
779
780                                 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
781
782                                 me = bpy.context.object.data
783                                 bm = bmesh.new()
784                                 bm.from_mesh(me)
785                                 cut_edges = []
786
787                                 for v in bm.edges:
788                                         if v.select:
789                                                 cut_edges.append(v.index)
790
791                                 bm.to_mesh(me)
792
793                                 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
794
795                                 #Store Intersection edges
796                                 bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')
797                                 bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
798
799                                 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
800
801                                 me = bpy.context.object.data
802                                 bm = bmesh.new()
803                                 bm.from_mesh(me)
804                                 int_edges = []
805
806                                 for v in bm.edges:
807                                         if v.select:
808                                                 int_edges.append(v.index)
809
810                                 bm.to_mesh(me)
811
812                                 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
813
814                                 #Modify Lists
815                                 for x in int_edges:
816                                         if x in boundry_edge:
817                                                 cut_edges.remove(x)
818
819                                 bpy.ops.mesh.select_all(action='DESELECT')
820
821                                 #Select the new edges to cut
822                                 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
823                                 mesh = bpy.context.active_object.data.edges
824
825                                 for f in cut_edges:
826                                         mesh[f].select = True
827
828                                 #Perform cut and select the cut line.
829                                 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
830                                 bpy.ops.mesh.subdivide(number_cuts = cuts, smoothness = smoothness, quadcorner = quad_corners)
831                                 bpy.ops.mesh.select_all(action='SELECT')
832                                 bpy.ops.mesh.region_to_loop()
833                                 bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
834                                 bpy.ops.mesh.select_all(action='INVERT')
835                                 bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
836                                 bpy.ops.mesh.loop_multi_select(ring=False)
837
838                                 #Store cut line.
839                                 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
840
841                                 me = bpy.context.object.data
842                                 bm = bmesh.new()
843                                 bm.from_mesh(me)
844                                 cut_line = []
845
846                                 for v in bm.edges:
847                                         if v.select:
848                                                 cut_line.append(v.index)
849
850                                 bm.to_mesh(me)
851
852                                 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
853
854                                 bpy.ops.mesh.reveal()
855                                 bpy.ops.mesh.select_all(action='DESELECT')
856
857                                 bpy.context.object.vertex_groups.active_index = tmp_hidden
858                                 bpy.ops.object.vertex_group_select()
859                                 bpy.ops.mesh.hide(unselected=True)
860
861
862                                 bpy.ops.mesh.select_all(action='DESELECT')
863
864
865                                 #Select Cutline
866                                 if cuts <= 1:
867                                         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
868                                         mesh = bpy.context.active_object.data.edges
869
870                                         for f in cut_line:
871                                                 mesh[f].select = True
872
873                                         bpy.ops.object.mode_set(mode='EDIT', toggle=False)
874
875                                 bpy.ops.object.vertex_group_remove(all=False)
876                                 bpy.ops.mesh.select_mode(use_extend=True, use_expand=False, type='FACE')
877
878                         elif sel_mode[0] == True and len(vert_sel) >= 2:
879                                 bpy.ops.mesh.vert_connect_path()
880
881                         elif sel_mode[1] == True and loopcut == False and len(edge_sel) != 0:
882                                 bpy.ops.mesh.subdivide(number_cuts = cuts, smoothness = smoothness, quadcorner = quad_corners)
883
884                         elif sel_mode[1] == True and loopcut == True and len(edge_sel) != 0:
885                                 bpy.ops.mesh.loop_multi_select(ring=True)
886                                 bpy.ops.mesh.subdivide(number_cuts = cuts, smoothness = smoothness, quadcorner = quad_corners)
887                         else:
888                             bpy.ops.mesh.select_all(action='DESELECT')
889                             bpy.ops.mesh.knife_tool("INVOKE_DEFAULT")
890
891                 else:
892                         self.report({'ERROR'}, "This one only works in Edit mode")
893
894                 return {'FINISHED'}
895
896
897
898 #Adds customAutoSmooth to the Addon
899
900 class customAutoSmooth(bpy.types.Operator):
901         """Set AutoSmooth angle"""
902         bl_idname = "object.custom_autosmooth"
903         bl_label = "Autosmooth"
904         bl_options = {'REGISTER', 'UNDO'}
905
906         angle: FloatProperty(name="AutoSmooth Angle", description="Set AutoSmooth angle", default= 30.0, min = 0.0, max = 180.0)
907
908
909         def execute(self, context):
910
911                 mode = bpy.context.active_object.mode
912
913                 if mode != 'OBJECT':
914                         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
915
916                 ob = bpy.context.selected_objects
917                 angle = self.angle
918                 angle = angle * (3.14159265359/180)
919
920                 bpy.ops.object.shade_smooth()
921
922                 for x in ob:
923                         x = x.name
924                         bpy.data.objects[x].data.use_auto_smooth = True
925                         bpy.data.objects[x].data.auto_smooth_angle = angle
926
927                 bpy.ops.object.mode_set(mode=mode, toggle=False)
928
929                 return {'FINISHED'}
930
931 #Adds shrinkwrapSmooth to the Addon
932
933 class shrinkwrapSmooth(bpy.types.Operator):
934         """Smooths the selected vertices while trying to keep the original shape with a shrinkwrap modifier. """
935         bl_idname = "mesh.shrinkwrap_smooth"
936         bl_label = "Shrinkwrap Smooth"
937         bl_options = {'REGISTER', 'UNDO'}
938
939         pin: BoolProperty(name="Pin Selection Border", description="Pins the outer edge of the selection.", default = True)
940         subsurf: IntProperty(name="Subsurf Levels", description="More reliable, but slower results", default = 0, min = 0, soft_max = 4)
941
942
943         def execute(self, context):
944
945                 iterate = 6
946                 pin = self.pin
947                 data = bpy.context.object.data.name
948
949                 # Set up for vertex weight
950                 bpy.context.scene.tool_settings.vertex_group_weight = 1
951                 v_grps = len(bpy.context.object.vertex_groups.items())
952
953                 bpy.ops.object.mode_set(mode = 'OBJECT', toggle = False)
954                 org_ob = bpy.context.object.name
955
956                 # Create intermediate object
957                 bpy.ops.object.mode_set(mode = 'OBJECT', toggle = False)
958                 bpy.ops.mesh.primitive_plane_add(radius=1, view_align=False, enter_editmode=False)
959                 bpy.context.object.data = bpy.data.meshes[data]
960                 tmp_ob = bpy.context.object.name
961
962
963                 bpy.ops.object.duplicate(linked=False)
964                 shrink_ob = bpy.context.object.name
965
966                 bpy.ops.object.select_all(action='DESELECT')
967                 bpy.ops.object.select_pattern(pattern=tmp_ob)
968                 bpy.context.view_layer.objects.active = bpy.data.objects[tmp_ob]
969
970                 bpy.ops.object.mode_set(mode = 'EDIT', toggle = False)
971                 bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
972
973                 if v_grps >= 1:
974                     for x in range(v_grps):
975                         bpy.ops.object.vertex_group_add()
976
977
978                 if pin == True:
979                         bpy.ops.object.vertex_group_assign_new()
980                         org_id = bpy.context.object.vertex_groups.active_index
981
982                         bpy.ops.object.vertex_group_assign_new()
983                         sel = bpy.context.object.vertex_groups.active.name
984                         sel_id = bpy.context.object.vertex_groups.active_index
985
986                         bpy.ops.mesh.region_to_loop()
987                         bpy.ops.object.vertex_group_remove_from(use_all_groups=False, use_all_verts=False)
988
989                         bpy.ops.mesh.select_all(action='SELECT')
990                         bpy.ops.mesh.region_to_loop()
991                         bpy.ops.object.vertex_group_remove_from(use_all_groups=False, use_all_verts=False)
992
993                         bpy.ops.mesh.select_all(action='DESELECT')
994                         bpy.ops.object.vertex_group_select(sel_id)
995
996
997                 else:
998                         bpy.ops.object.vertex_group_assign_new()
999                         sel = bpy.context.object.vertex_groups.active.name
1000
1001
1002                 for x in range(iterate):
1003                         bpy.ops.object.modifier_add(type='SHRINKWRAP')
1004                         mod_id = (len(bpy.context.object.modifiers)-1)
1005                         shrink_name = bpy.context.object.modifiers[mod_id].name
1006
1007                         bpy.context.object.modifiers[shrink_name].target = bpy.data.objects[shrink_ob]
1008                         bpy.context.object.modifiers[shrink_name].vertex_group = sel
1009
1010                         bpy.context.object.modifiers[shrink_name].wrap_method = 'PROJECT'
1011                         bpy.context.object.modifiers[shrink_name].use_negative_direction = True
1012                         bpy.context.object.modifiers[shrink_name].subsurf_levels = self.subsurf
1013
1014
1015                         bpy.ops.mesh.vertices_smooth(factor=1, repeat=1)
1016
1017
1018                         bpy.ops.object.mode_set(mode = 'OBJECT', toggle = False)
1019                         bpy.ops.object.convert(target='MESH')
1020                         bpy.ops.object.mode_set(mode = 'EDIT', toggle = False)
1021
1022
1023                 bpy.ops.object.mode_set(mode = 'OBJECT', toggle = False)
1024
1025
1026
1027                 bpy.ops.object.vertex_group_remove(all = False)
1028                 bpy.ops.object.modifier_remove(modifier=shrink_name)
1029
1030                 bpy.ops.object.select_all(action='DESELECT')
1031                 bpy.ops.object.select_pattern(pattern=shrink_ob)
1032                 bpy.context.view_layer.objects.active = bpy.data.objects[shrink_ob]
1033
1034                 #Delete all geo inside Shrink_Object
1035                 bpy.ops.object.mode_set(mode = 'EDIT', toggle = False)
1036                 bpy.ops.mesh.select_all(action='SELECT')
1037                 bpy.ops.mesh.delete(type='VERT')
1038                 bpy.ops.object.mode_set(mode = 'OBJECT', toggle = False)
1039
1040                 bpy.ops.object.delete(use_global=True)
1041
1042                 bpy.ops.object.select_pattern(pattern=tmp_ob)
1043                 bpy.context.view_layer.objects.active = bpy.data.objects[tmp_ob]
1044
1045
1046                 bpy.ops.object.mode_set(mode = 'EDIT', toggle = False)
1047
1048                 if pin == True:
1049                         bpy.ops.mesh.select_all(action='DESELECT')
1050                         bpy.ops.object.vertex_group_select(org_id)
1051
1052                 bpy.ops.object.mode_set(mode = 'OBJECT', toggle = False)
1053
1054                 bpy.ops.object.delete(use_global=False)
1055
1056
1057                 bpy.ops.object.select_pattern(pattern=org_ob)
1058                 bpy.context.view_layer.objects.active = bpy.data.objects[org_ob]
1059
1060                 bpy.ops.object.mode_set(mode = 'EDIT', toggle = False)
1061
1062                 # Fix for Blender remembering the previous selection
1063                 bpy.ops.object.vertex_group_assign_new()
1064                 bpy.ops.object.vertex_group_remove(all = False)
1065
1066
1067                 return {'FINISHED'}
1068
1069 #Adds buildCorner to the Addon
1070
1071 class buildCorner(bpy.types.Operator):
1072         """Builds corner topology. Good for converting ngons"""
1073         bl_idname = "mesh.build_corner"
1074         bl_label = "Build Corner"
1075         bl_options = {'REGISTER', 'UNDO'}
1076
1077         offset: IntProperty()
1078
1079         def modal(self, context, event):
1080
1081                 if event.type == 'MOUSEMOVE':
1082
1083                         delta = self.offset - event.mouse_x
1084
1085                         if delta >= 0:
1086                                 offset = 1
1087                         else:
1088                                 offset = 0
1089
1090                         bpy.ops.mesh.edge_face_add()
1091
1092                         bpy.ops.mesh.poke()
1093                         bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
1094                         bpy.ops.object.vertex_group_assign_new()
1095                         sel_id = bpy.context.object.vertex_groups.active_index
1096
1097                         bpy.ops.mesh.region_to_loop()
1098                         bpy.ops.object.vertex_group_remove_from()
1099
1100                         bpy.ops.mesh.select_nth(nth=2, skip=1, offset=offset)
1101
1102                         bpy.ops.object.vertex_group_select(sel_id)
1103
1104                         bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
1105                         bpy.ops.mesh.dissolve_mode(use_verts=False)
1106
1107                         bpy.ops.mesh.select_all(action='DESELECT')
1108                         bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
1109                         bpy.ops.object.vertex_group_select()
1110                         bpy.ops.mesh.select_more()
1111
1112                         bpy.ops.object.vertex_group_remove(all = False)
1113                         bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')
1114
1115
1116                 elif event.type == 'LEFTMOUSE':
1117                         return {'FINISHED'}
1118
1119                 elif event.type in {'RIGHTMOUSE', 'ESC'}:
1120                     bpy.ops.ed.undo()
1121                     return {'CANCELLED'}
1122
1123                 return {'RUNNING_MODAL'}
1124
1125         def invoke(self, context, event):
1126                 if context.object:
1127
1128                         # Check selection
1129
1130                         bpy.ops.mesh.edge_face_add()
1131                         bpy.ops.mesh.region_to_loop()
1132
1133                         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1134
1135                         me = bpy.context.object.data
1136                         bm = bmesh.new()     # create an empty BMesh
1137                         bm.from_mesh(me)     # fill it in from a Mesh
1138
1139                         face_sel = []
1140                         edge_sel = []
1141
1142
1143                         for v in bm.faces:
1144                                 if v.select:
1145                                         face_sel.append(v.index)
1146                         for v in bm.edges:
1147                                 if v.select:
1148                                         edge_sel.append(v.index)
1149
1150
1151
1152                         bm.to_mesh(me)
1153                         bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1154                         bpy.ops.mesh.loop_to_region()
1155
1156
1157                         ###################################
1158
1159                         edge_sel = len(edge_sel)
1160
1161                         if edge_sel == 4:
1162                                 return {'FINISHED'}
1163
1164                         elif edge_sel%2 == 0:
1165                                 self.offset = event.mouse_x
1166                                 context.window_manager.modal_handler_add(self)
1167                                 return {'RUNNING_MODAL'}
1168
1169                         #elif edge_sel == 5:
1170                         #    bpy.ops.mesh.quads_convert_to_tris(quad_method='BEAUTY', ngon_method='BEAUTY')
1171                         #    bpy.ops.mesh.tris_convert_to_quads(face_threshold=3.14159, shape_threshold=3.14159)
1172                         #    return {'FINISHED'}
1173
1174                         else:
1175                                 bpy.ops.mesh.poke()
1176                                 bpy.ops.mesh.quads_convert_to_tris(quad_method='BEAUTY', ngon_method='BEAUTY')
1177                                 bpy.ops.mesh.tris_convert_to_quads(face_threshold=3.14159, shape_threshold=3.14159)
1178                                 return {'FINISHED'}
1179
1180                 else:
1181                         self.report({'WARNING'}, "No active object, could not finish")
1182                         return {'CANCELLED'}
1183
1184
1185 class drawPoly(bpy.types.Operator):
1186     """Draw a polygon"""
1187     bl_idname = "mesh.draw_poly"
1188     bl_label = "Draw Poly"
1189
1190     cursor_co: FloatVectorProperty()
1191     vert_count = 0
1192     manip: BoolProperty()
1193     vgrp: IntProperty()
1194     sel_mode = BoolVectorProperty()
1195     cursor_depth: BoolProperty()
1196     snap: BoolProperty()
1197
1198
1199     def modal(self, context, event):
1200
1201         # set header gui
1202         context.area.tag_redraw()
1203         context.area.header_text_set("LMB = Create New Point, SHIFT = Flip Normals, RMB / ENTER = Accept NGon, MMB = Accept QuadFill")
1204
1205
1206         mesh = bpy.context.active_object.data
1207
1208         if event.type == 'LEFTMOUSE':
1209             if event.value == 'PRESS':
1210
1211
1212                 bpy.ops.view3d.cursor3d('INVOKE_DEFAULT')
1213
1214                 obj = bpy.context.active_object
1215                 vert_co = bpy.context.scene.cursor_location
1216                 world = obj.matrix_world.inverted_safe()
1217
1218                 bpy.ops.mesh.select_all(action='DESELECT')
1219                 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1220
1221                 me = bpy.context.object.data
1222                 bm = bmesh.new()
1223                 bm.from_mesh(me)
1224
1225                 # Add new vert
1226                 new_vert = bm.verts.new(vert_co)
1227                 new_vert.co = world*new_vert.co
1228                 new_vert_id = new_vert.index
1229                 self.vert_count += 1
1230
1231                 if self.vert_count >= 2:
1232                     bm.verts.ensure_lookup_table()
1233                     set_of_verts = set(bm.verts[i] for i in range(-2,0))
1234                     bm.edges.new(set_of_verts)
1235
1236                 # Get index of first and last vertex
1237                 first_index = len(bm.verts)-self.vert_count
1238                 second_index = first_index+1
1239                 third_index = first_index+2
1240                 second_to_last_index = len(bm.verts)-2
1241
1242
1243                 bm.to_mesh(me)
1244                 bm.free()
1245
1246                 mesh.vertices[new_vert_id].select = True
1247                 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1248                 bpy.context.scene.cursor_location = self.cursor_co
1249
1250
1251                 if self.vert_count >= 4:
1252                     bpy.ops.object.vertex_group_assign()
1253
1254                 if self.vert_count == 3:
1255                     # remove second vertex from group
1256                     bpy.ops.mesh.select_all(action='DESELECT')
1257                     bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1258                     mesh.vertices[first_index].select = True
1259                     mesh.vertices[third_index].select = True
1260                     bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1261                     bpy.ops.object.vertex_group_assign()
1262                     bpy.ops.mesh.select_more()
1263
1264                 if self.vert_count == 2:
1265                     bpy.ops.mesh.select_more()
1266
1267                 if self.vert_count >= 4:
1268                     # make core poly
1269                     bpy.ops.mesh.select_all(action='DESELECT')
1270                     bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1271                     mesh.vertices[first_index].select = True
1272                     mesh.vertices[second_index].select = True
1273                     mesh.vertices[third_index].select = True
1274                     bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1275                     bpy.ops.mesh.edge_face_add()
1276
1277                 if self.vert_count == 4:
1278                     bpy.ops.mesh.select_all(action='DESELECT')
1279                     bpy.ops.object.vertex_group_select(self.vgrp)
1280                     bpy.ops.mesh.edge_face_add()
1281
1282                     # Remove remaining core edge
1283                     bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1284                     mesh.vertices[second_index].select = True
1285                     bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1286                     bpy.ops.mesh.edge_face_add()
1287
1288
1289                 if self.vert_count >= 5:
1290                     #bpy.ops.object.vertex_group_assign()
1291                     bpy.ops.mesh.select_all(action='DESELECT')
1292
1293                     # Remove Last Edge
1294                     bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1295                     mesh.vertices[first_index].select = True
1296                     mesh.vertices[second_to_last_index].select = True
1297                     bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1298                     bpy.ops.mesh.delete(type='EDGE')
1299
1300                     # Fill in rest of face
1301                     bpy.ops.object.vertex_group_select(self.vgrp)
1302                     bpy.ops.mesh.edge_face_add()
1303
1304
1305                     # Remove remaining core edge
1306                     bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1307                     mesh.vertices[second_index].select = True
1308                     bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1309                     bpy.ops.mesh.edge_face_add()
1310                     bpy.ops.mesh.flip_normals()
1311
1312
1313             #return {'FINISHED'}
1314         elif event.type == 'MIDDLEMOUSE':
1315
1316             # reset header gui
1317             context.area.tag_redraw()
1318             context.area.header_text_set(None)
1319
1320             # Convert to Quads
1321             bpy.ops.mesh.quads_convert_to_tris(quad_method='BEAUTY', ngon_method='BEAUTY')
1322             bpy.ops.mesh.tris_convert_to_quads()
1323             bpy.ops.mesh.tris_convert_to_quads(face_threshold=3.14159, shape_threshold=3.14159)
1324
1325
1326             # restore selection mode and manipulator
1327             bpy.context.tool_settings.mesh_select_mode = self.sel_mode
1328             bpy.context.space_data.show_manipulator = self.manip
1329             bpy.context.preferences.input.use_mouse_depth_cursor = self.cursor_depth
1330             bpy.context.scene.tool_settings.use_snap = self.snap
1331
1332
1333             # Remove and make sure vertex group data is gone
1334             bpy.ops.object.vertex_group_remove_from(use_all_verts=True)
1335             bpy.ops.object.vertex_group_remove()
1336             bpy.ops.object.vertex_group_assign_new()
1337             bpy.context.object.vertex_groups.active.name = "drawPoly_temp"
1338             bpy.ops.object.vertex_group_remove()
1339
1340
1341             return {'CANCELLED'}
1342
1343         elif event.type in {'RIGHTMOUSE', 'ESC', 'SPACE'}:
1344
1345             # reset header gui
1346             context.area.tag_redraw()
1347             context.area.header_text_set(None)
1348
1349
1350             # restore selection mode and manipulator
1351             bpy.context.tool_settings.mesh_select_mode = self.sel_mode
1352             bpy.context.space_data.show_manipulator = self.manip
1353             bpy.context.preferences.input.use_mouse_depth_cursor = self.cursor_depth
1354             bpy.context.scene.tool_settings.use_snap = self.snap
1355
1356
1357             # Remove and make sure vertex group data is gone
1358             bpy.ops.object.vertex_group_remove_from(use_all_verts=True)
1359             bpy.ops.object.vertex_group_remove()
1360             bpy.ops.object.vertex_group_assign_new()
1361             bpy.context.object.vertex_groups.active.name = "drawPoly_temp"
1362             bpy.ops.object.vertex_group_remove()
1363
1364
1365             return {'CANCELLED'}
1366
1367         elif event.type == 'LEFT_SHIFT' or event.type == 'RIGHT_SHIFT':
1368             bpy.ops.mesh.flip_normals()
1369
1370             return {'PASS_THROUGH'}
1371
1372         elif event.type == 'LEFT_CTRL' or event.type == 'RIGHT_CTRL' :
1373             if bpy.context.preferences.input.use_mouse_depth_cursor == True:
1374                 bpy.context.preferences.input.use_mouse_depth_cursor = False
1375                 bpy.context.scene.tool_settings.use_snap = False
1376             else:
1377                 bpy.context.preferences.input.use_mouse_depth_cursor = True
1378                 bpy.context.scene.tool_settings.use_snap = True
1379             return {'PASS_THROUGH'}
1380
1381         return {'RUNNING_MODAL'}
1382
1383     def invoke(self, context, event):
1384
1385
1386         sel_ob = len(bpy.context.selected_objects)
1387
1388
1389         if sel_ob >= 1:
1390             sel_type = bpy.context.object.type
1391
1392             if sel_type == 'MESH':
1393                 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1394
1395             else:
1396                 self.report({'WARNING'}, "Active object is not a mesh.")
1397                 return {'CANCELLED'}
1398
1399
1400         elif sel_ob == 0:
1401             bpy.ops.mesh.primitive_plane_add()
1402             bpy.context.selected_objects[0].name = "polyDraw"
1403             bpy.context.selected_objects[0].data.name = "polyDraw"
1404             bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1405             bpy.ops.mesh.select_all(action='SELECT')
1406             bpy.ops.mesh.delete(type='VERT')
1407
1408
1409
1410
1411         # Store selection mode, snap and manipulator settings
1412         self.sel_mode = bpy.context.tool_settings.mesh_select_mode[:]
1413         bpy.context.tool_settings.mesh_select_mode = True, False, False
1414         self.manip = bpy.context.space_data.show_manipulator
1415         bpy.context.space_data.show_manipulator = False
1416         self.cursor_depth = bpy.context.preferences.input.use_mouse_depth_cursor
1417         bpy.context.preferences.input.use_mouse_depth_cursor = False
1418         self.snap = bpy.context.scene.tool_settings.use_snap
1419         bpy.context.scene.tool_settings.use_snap = False
1420
1421         bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1422
1423         bpy.ops.mesh.select_all(action='DESELECT')
1424         bpy.ops.object.vertex_group_assign_new()
1425         self.vgrp = bpy.context.object.vertex_groups.active_index
1426         bpy.context.object.vertex_groups.active.name = "drawPoly_temp"
1427
1428         self.cursor_co = bpy.context.scene.cursor_location
1429
1430
1431         context.window_manager.modal_handler_add(self)
1432         return {'RUNNING_MODAL'}
1433
1434 class toggleSilhouette(bpy.types.Operator):
1435     """Turns everything black so that you can evaluate the overall shape. Useful when designing"""
1436     bl_idname = "object.toggle_silhouette"
1437     bl_label = "Toggle Silhouette"
1438
1439
1440     diff_col: FloatVectorProperty(default = (0.226, 0.179, 0.141))
1441     disp_mode: StringProperty(default = 'SOLID')
1442     matcap: BoolProperty(default = False)
1443     only_render: BoolProperty(default = False)
1444
1445     def execute(self, context):
1446
1447
1448         light_check = bpy.context.preferences.system.solid_lights[0].use
1449
1450         if light_check == True:
1451             # Set Lights to Off
1452             bpy.context.preferences.system.solid_lights[0].use = False
1453             bpy.context.preferences.system.solid_lights[1].use = False
1454
1455             # Store variables
1456             self.diff_col = bpy.context.preferences.system.solid_lights[2].diffuse_color
1457             self.disp_mode = bpy.context.space_data.viewport_shade
1458             self.matcap = bpy.context.space_data.use_matcap
1459             self.only_render = bpy.context.space_data.show_only_render
1460
1461             bpy.context.preferences.system.solid_lights[2].diffuse_color = 0,0,0
1462             bpy.context.space_data.viewport_shade = 'SOLID'
1463             bpy.context.space_data.use_matcap = False
1464             bpy.context.space_data.show_only_render = True
1465
1466         else:
1467             bpy.context.preferences.system.solid_lights[0].use = True
1468             bpy.context.preferences.system.solid_lights[1].use = True
1469             bpy.context.preferences.system.solid_lights[2].diffuse_color = self.diff_col
1470             bpy.context.space_data.viewport_shade = self.disp_mode
1471             bpy.context.space_data.use_matcap = self.matcap
1472             bpy.context.space_data.show_only_render = self.only_render
1473
1474         return {'FINISHED'}
1475
1476
1477
1478 #Adds growLoop to the Addon
1479
1480 class growLoop(bpy.types.Operator):
1481         """Grows the selected edges in both directions """
1482         bl_idname = "mesh.grow_loop"
1483         bl_label = "Grow Loop"
1484         bl_options = {'REGISTER', 'UNDO'}
1485
1486         grow: IntProperty(name="Grow Selection", description="How much to grow selection", default= 1, min=1, soft_max=10)
1487
1488         def execute(self, context):
1489
1490                 grow = self.grow
1491                 sel_mode = bpy.context.tool_settings.mesh_select_mode[:]
1492
1493                 for x in range(grow):
1494                         if sel_mode[2] == True:
1495
1496                                 edge_sel = []
1497                                 border = []
1498                                 interior = []
1499                                 face_org = []
1500                                 face_loop = []
1501                                 face_grow = []
1502                                 face_sel = []
1503                                 mesh_edges = bpy.context.active_object.data.edges
1504                                 mesh_faces = bpy.context.active_object.data.polygons
1505
1506                                 bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')
1507
1508                                 me = bpy.context.object.data
1509                                 bm = bmesh.from_edit_mesh(me)
1510
1511                                 for e in bm.edges:
1512                                         if e.select:
1513                                                 edge_sel.append(e.index)
1514
1515                                 for f in bm.faces:
1516                                         if f.select:
1517                                                 face_org.append(f.index)
1518
1519                                 bpy.ops.mesh.region_to_loop()
1520
1521                                 for e in bm.edges:
1522                                         if e.select:
1523                                                 border.append(e.index)
1524
1525
1526
1527                                 for e in edge_sel:
1528                                         if e not in border:
1529                                                 interior.append(e)
1530
1531                                 bmesh.update_edit_mesh(me, True, False)
1532
1533
1534                                 bpy.ops.mesh.select_all(action='DESELECT')
1535
1536                                 #Select the interior edges
1537                                 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1538
1539
1540                                 for e in interior:
1541                                         mesh_edges[e].select = True
1542
1543                                 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1544                                 bpy.ops.mesh.loop_multi_select(ring=True)
1545                                 bpy.ops.mesh.select_mode(use_extend=False, use_expand=True, type='FACE')
1546
1547                                 me = bpy.context.object.data
1548                                 bm = bmesh.from_edit_mesh(me)
1549
1550                                 for f in bm.faces:
1551                                         if f.select:
1552                                                 face_loop.append(f.index)
1553
1554                                 bmesh.update_edit_mesh(me, True, False)
1555
1556                                 bpy.ops.mesh.select_all(action='DESELECT')
1557
1558
1559                                 # Select original faces
1560                                 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1561                                 for x in face_org:
1562                                         mesh_faces[x].select = True
1563                                 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1564
1565
1566                                 bpy.ops.mesh.select_more(use_face_step=False)
1567
1568                                 me = bpy.context.object.data
1569                                 bm = bmesh.from_edit_mesh(me)
1570
1571                                 for f in bm.faces:
1572                                         if f.select:
1573                                                 face_grow.append(f.index)
1574
1575                                 for f in face_grow:
1576                                         if f in face_loop:
1577                                                 face_sel.append(f)
1578
1579                                 for f in face_org:
1580                                         face_sel.append(f)
1581
1582                                 bmesh.update_edit_mesh(me, True, False)
1583
1584                                 bpy.ops.mesh.select_all(action='DESELECT')
1585
1586                                 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1587
1588                                 for f in face_sel:
1589                                         mesh_faces[f].select = True
1590
1591                                 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1592
1593                         else:
1594                                 mesh = bpy.context.active_object.data.edges
1595
1596                                 me = bpy.context.object.data
1597                                 bm = bmesh.from_edit_mesh(me)
1598                                 org_sel = []
1599                                 grow_sel = []
1600                                 loop_sel = []
1601                                 sel = []
1602
1603                                 bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
1604
1605                                 for e in bm.edges:
1606                                         if e.select:
1607                                                 org_sel.append(e.index)
1608
1609                                 bpy.ops.mesh.select_more(use_face_step=False)
1610
1611                                 for e in bm.edges:
1612                                         if e.select:
1613                                                 grow_sel.append(e.index)
1614
1615                                 bpy.ops.mesh.select_all(action='DESELECT')
1616
1617                                 bmesh.update_edit_mesh(me, True, False)
1618
1619                                 # Select the original edges
1620                                 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1621                                 for e in org_sel:
1622                                         mesh[e].select = True
1623                                 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1624
1625
1626                                 me = bpy.context.object.data
1627                                 bm = bmesh.from_edit_mesh(me)
1628                                 bpy.ops.mesh.loop_multi_select(ring=False)
1629
1630                                 for e in bm.edges:
1631                                         if e.select:
1632                                                 loop_sel.append(e.index)
1633
1634                                 bmesh.update_edit_mesh(me, True, False)
1635
1636                                 bpy.ops.mesh.select_all(action='DESELECT')
1637
1638                                 for x in loop_sel:
1639                                         if x in grow_sel:
1640                                                 sel.append(x)
1641
1642                                 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1643                                 for e in sel:
1644                                         mesh[e].select = True
1645                                 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1646
1647                         bpy.context.tool_settings.mesh_select_mode = sel_mode
1648
1649                 return {'FINISHED'}
1650
1651 #Adds extendLoop to the Addon
1652
1653 class extendLoop(bpy.types.Operator):
1654         """Uses the active face or edge to extends the selection in one direction"""
1655         bl_idname = "mesh.extend_loop"
1656         bl_label = "Extend Loop"
1657         bl_options = {'REGISTER', 'UNDO'}
1658
1659         extend: IntProperty(name="Extend Selection", description="How much to extend selection", default= 1, min=1, soft_max=10)
1660
1661         def execute(self, context):
1662
1663                 sel_mode = bpy.context.tool_settings.mesh_select_mode[:]
1664                 extend = self.extend
1665
1666                 for x in range(extend):
1667                     if sel_mode[2] == True:
1668
1669                         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1670                         active_face = bpy.context.object.data.polygons.active # find active face
1671                         bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1672
1673                         edge_sel = []
1674                         interior = []
1675                         face_org = []
1676                         face_loop = []
1677                         face_grow = []
1678                         face_sel = []
1679                         active_edges = []
1680
1681                         # Get face selection
1682                         me = bpy.context.object.data
1683                         bm = bmesh.from_edit_mesh(me)
1684
1685                         for f in bm.faces:
1686                                 if f.select:
1687                                         face_org.append(f.index)
1688
1689                         face_org.remove(active_face)
1690
1691
1692                         bmesh.update_edit_mesh(me, True, False)
1693
1694                         bpy.ops.mesh.select_all(action='DESELECT')
1695                         mesh = bpy.context.active_object.data.polygons
1696
1697                         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1698                         for x in face_org:
1699                                 mesh[x].select = True
1700                         bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1701
1702
1703
1704                         # Get edge selection
1705                         me = bpy.context.object.data
1706                         bm = bmesh.from_edit_mesh(me)
1707
1708                         for e in bm.edges:
1709                                 if e.select:
1710                                         edge_sel.append(e.index)
1711
1712
1713                         bmesh.update_edit_mesh(me, True, False)
1714
1715
1716                         # Select Active Face
1717                         bpy.ops.mesh.select_all(action='DESELECT')
1718                         mesh = bpy.context.active_object.data.polygons
1719
1720                         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1721                         mesh[active_face].select = True
1722                         bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1723                         bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
1724
1725                         me = bpy.context.object.data
1726                         bm = bmesh.from_edit_mesh(me)
1727
1728
1729                         # Store the interior edge
1730
1731                         for e in bm.edges:
1732                                 if e.select:
1733                                         active_edges.append(e.index)
1734
1735
1736                         for e in active_edges:
1737                                 if e in edge_sel:
1738                                         interior.append(e)
1739
1740                         bmesh.update_edit_mesh(me, True, False)
1741
1742
1743                         bpy.ops.mesh.select_all(action='DESELECT')
1744
1745                         #Select the interior edges
1746                         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1747
1748                         mesh = bpy.context.active_object.data.edges
1749
1750                         for e in interior:
1751                                 mesh[e].select = True
1752
1753                         bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1754
1755
1756                         bpy.ops.mesh.loop_multi_select(ring=True)
1757                         bpy.ops.mesh.select_mode(use_extend=False, use_expand=True, type='FACE')
1758
1759
1760                         me = bpy.context.object.data
1761                         bm = bmesh.from_edit_mesh(me)
1762
1763                         for f in bm.faces:
1764                                 if f.select:
1765                                         face_loop.append(f.index)
1766
1767
1768                         bmesh.update_edit_mesh(me, True, False)
1769
1770                         bpy.ops.mesh.select_all(action='DESELECT')
1771
1772                         # Select active face
1773                         mesh = bpy.context.active_object.data.polygons
1774
1775                         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1776                         mesh[active_face].select = True
1777                         bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1778                         bpy.ops.mesh.select_more(use_face_step=False)
1779
1780
1781                         face_org.append(active_face)
1782
1783                         me = bpy.context.object.data
1784                         bm = bmesh.from_edit_mesh(me)
1785
1786                         for f in bm.faces:
1787                                 if f.select:
1788                                         face_grow.append(f.index)
1789
1790                         for f in face_grow:
1791                                 if f in face_loop:
1792                                         face_sel.append(f)
1793
1794                         for f in face_sel:
1795                                 if f not in face_org:
1796                                         active_face = f
1797
1798                         for f in face_org:
1799                                 face_sel.append(f)
1800
1801                         bmesh.update_edit_mesh(me, True, False)
1802
1803                         bpy.ops.mesh.select_all(action='DESELECT')
1804
1805                         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1806
1807                         for f in face_sel:
1808                                 mesh[f].select = True
1809                         bpy.context.object.data.polygons.active = active_face
1810
1811                         bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1812
1813                     elif sel_mode[1] == True:
1814
1815                         mesh = bpy.context.active_object.data
1816                         org_sel = []
1817                         grow_sel = []
1818                         loop_sel = []
1819                         sel = []
1820                         org_verts = []
1821                         active_verts = []
1822
1823                         # Get active edge
1824                         me = bpy.context.object.data
1825                         bm = bmesh.from_edit_mesh(me)
1826
1827                         for x in reversed(bm.select_history):
1828                                 if isinstance(x, bmesh.types.BMEdge):
1829                                         active_edge = x.index
1830                                         break
1831
1832                         # Store the originally selected edges
1833                         for e in bm.edges:
1834                                 if e.select:
1835                                         org_sel.append(e.index)
1836
1837
1838                         bmesh.update_edit_mesh(me, True, False)
1839
1840                         # Select active edge
1841                         bpy.ops.mesh.select_all(action='DESELECT')
1842                         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1843                         mesh.edges[active_edge].select = True
1844                         bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1845
1846                         # Get verts of active edge
1847                         bm = bmesh.from_edit_mesh(me)
1848
1849                         for v in bm.verts:
1850                                 if v.select:
1851                                         active_verts.append(v.index)
1852
1853                         bmesh.update_edit_mesh(me, True, False)
1854
1855                         # Select original selection minus active edge
1856                         bpy.ops.mesh.select_all(action='DESELECT')
1857                         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1858                         for x in org_sel:
1859                             mesh.edges[x].select = True
1860                         mesh.edges[active_edge].select = False
1861                         bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1862
1863                         bm = bmesh.from_edit_mesh(me)
1864
1865                         # Store the original vertices minus active edge
1866                         for v in bm.verts:
1867                             if v.select:
1868                                 org_verts.append(v.index)
1869
1870
1871                         # Compare verts
1872                         for x in active_verts:
1873                             if x in org_verts:
1874                                 active_verts.remove(x)
1875
1876                         bmesh.update_edit_mesh(me, True, False)
1877
1878                         # Select end vertex
1879                         bpy.ops.mesh.select_all(action='DESELECT')
1880                         bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
1881                         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1882                         mesh.vertices[active_verts[0]].select = True
1883                         bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1884
1885
1886                         # Grow the end vertex and store the edges
1887                         bpy.ops.mesh.select_more(use_face_step=False)
1888                         bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
1889                         bm = bmesh.from_edit_mesh(me)
1890
1891                         for e in bm.edges:
1892                                 if e.select:
1893                                         grow_sel.append(e.index)
1894
1895                         bmesh.update_edit_mesh(me, True, False)
1896                         bpy.ops.mesh.select_all(action='DESELECT')
1897
1898                         # Run loop of the active edges and store it
1899                         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1900                         mesh.edges[active_edge].select = True
1901                         bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1902                         bpy.ops.mesh.loop_multi_select(ring=False)
1903
1904                         me = bpy.context.object.data
1905                         bm = bmesh.from_edit_mesh(me)
1906
1907                         for e in bm.edges:
1908                                 if e.select:
1909                                         loop_sel.append(e.index)
1910
1911                         bmesh.update_edit_mesh(me, True, False)
1912                         bpy.ops.mesh.select_all(action='DESELECT')
1913
1914                         # Compare loop_sel vs grow_sel
1915                         for x in loop_sel:
1916                                 if x in grow_sel:
1917                                         sel.append(x)
1918
1919
1920                         # Add original selection to new selection
1921
1922                         for x in org_sel:
1923                             if x not in sel:
1924                                 sel.append(x)
1925
1926
1927                         # Compare org_sel with sel to get the active edge
1928                         for x in sel:
1929                             if x not in org_sel:
1930                                 active_edge = x
1931
1932                         # Select the resulting edges
1933                         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
1934                         for e in sel:
1935                                 mesh.edges[e].select = True
1936                         bpy.ops.object.mode_set(mode='EDIT', toggle=False)
1937
1938                         # Set the new active edge
1939                         bm = bmesh.from_edit_mesh(me)
1940
1941                         bm.edges.ensure_lookup_table()
1942                         bm.select_history.add(bm.edges[active_edge])
1943                         bmesh.update_edit_mesh(me, True, False)
1944
1945
1946                 return {'FINISHED'}
1947
1948
1949 #Adds extendLoop to the Addon
1950
1951 class shrinkLoop(bpy.types.Operator):
1952         """Shrink the selected loop"""
1953         bl_idname = "mesh.shrink_loop"
1954         bl_label = "Shrink Loop"
1955         bl_options = {'REGISTER', 'UNDO'}
1956
1957         shrink: IntProperty(name="Shrink Selection", description="How much to shrink selection", default= 1, min=1, soft_max=15)
1958
1959         def execute(self, context):
1960
1961                 sel_mode = bpy.context.tool_settings.mesh_select_mode[:]
1962                 shrink = self.shrink
1963
1964                 for x in range(shrink):
1965                     if sel_mode[2] == True:
1966                         me = bpy.context.object.data
1967                         bm = bmesh.from_edit_mesh(me)
1968                         mesh = bpy.context.active_object.data
1969
1970                         sel = []
1971                         edge_dic = {}
1972                         vert_list = []
1973                         end_verts = []
1974                         org_faces = []
1975                         cur_faces = []
1976                         new_faces = []
1977
1978                         # Store edges and verts
1979                         for e in bm.edges:
1980                             if e.select:
1981                                 sel.append(e.index)
1982
1983                                 # Populate vert_list
1984                                 vert_list.append(e.verts[0].index)
1985                                 vert_list.append(e.verts[1].index)
1986
1987                                 # Store dictionary
1988                                 edge_dic[e.index] = [e.verts[0].index, e.verts[1].index]
1989
1990                         # Store original faces
1991                         for f in bm.faces:
1992                             if f.select:
1993                                 org_faces.append(f.index)
1994
1995                         # Store end verts
1996                         for v in vert_list:
1997                             if vert_list.count(v) == 2:
1998                                 end_verts.append(v)
1999
2000                         # Check verts in dictionary
2001                         for key, value in edge_dic.items():
2002                             if value[0] in end_verts:
2003                                 sel.remove(key)
2004                                 continue
2005                             if value[1] in end_verts:
2006                                 sel.remove(key)
2007
2008
2009                         bmesh.update_edit_mesh(me, True, False)
2010
2011                         # Select the resulting edges
2012                         bpy.ops.mesh.select_all(action='DESELECT')
2013                         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
2014
2015                         for e in sel:
2016                             mesh.edges[e].select = True
2017                         bpy.ops.object.mode_set(mode='EDIT', toggle=False)
2018
2019                         bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
2020                         bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')
2021
2022                         bm = bmesh.from_edit_mesh(me)
2023
2024                         # Store current faces
2025                         for f in bm.faces:
2026                             if f.select:
2027                                 cur_faces.append(f.index)
2028
2029                         # Compare current and original faces
2030                         for x in org_faces:
2031                             if x in cur_faces:
2032                                 new_faces.append(x)
2033
2034                         bmesh.update_edit_mesh(me, True, False)
2035
2036                         # Select the resulting faces
2037                         bpy.ops.mesh.select_all(action='DESELECT')
2038                         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
2039
2040                         for e in new_faces:
2041                             mesh.polygons[e].select = True
2042                         bpy.ops.object.mode_set(mode='EDIT', toggle=False)
2043
2044                     else:
2045                         me = bpy.context.object.data
2046                         bm = bmesh.from_edit_mesh(me)
2047
2048                         sel = []
2049                         edge_dic = {}
2050                         vert_list = []
2051                         end_verts = []
2052
2053                         # Store edges and verts in dictionary
2054                         for e in bm.edges:
2055                             if e.select:
2056                                 sel.append(e.index)
2057
2058                                 # Populate vert_list
2059                                 vert_list.append(e.verts[0].index)
2060                                 vert_list.append(e.verts[1].index)
2061
2062                                 # Store dictionary
2063                                 edge_dic[e.index] = [e.verts[0].index, e.verts[1].index]
2064
2065                         # Store end verts
2066                         for v in vert_list:
2067                             if vert_list.count(v) == 1:
2068                                 end_verts.append(v)
2069
2070                         # Check verts in dictionary
2071                         for key, value in edge_dic.items():
2072                             if value[0] in end_verts:
2073                                 sel.remove(key)
2074                                 continue
2075                             if value[1] in end_verts:
2076                                 sel.remove(key)
2077
2078
2079                         bmesh.update_edit_mesh(me, True, False)
2080
2081                         # Select the resulting edges
2082                         bpy.ops.mesh.select_all(action='DESELECT')
2083
2084                         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
2085                         mesh = bpy.context.active_object.data.edges
2086                         for e in sel:
2087                             mesh[e].select = True
2088                         bpy.ops.object.mode_set(mode='EDIT', toggle=False)
2089
2090
2091                 return {'FINISHED'}
2092
2093 class paintSelect(bpy.types.Operator):
2094     """Click and drag to select"""
2095     bl_idname = "view3d.select_paint"
2096     bl_label = "Paint Select"
2097     bl_options = {'REGISTER', 'UNDO'}
2098
2099     deselect: BoolProperty(default = False, description = 'Deselect objects, polys, edges or verts')
2100     toggle: BoolProperty(default = False, description = 'Toggles the selection. NOTE: this option can be slow on heavy meshes')
2101     sel_before: IntProperty(description = 'Do Not Touch', options = {'HIDDEN'})
2102     sel_after: IntProperty(description = 'Do Not Touch', options = {'HIDDEN'})
2103
2104     def modal(self, context, event):
2105
2106         #if event.type == 'MOUSEMOVE':
2107         refresh = event.mouse_x
2108
2109
2110         if self.deselect == False:
2111             bpy.ops.view3d.select('INVOKE_DEFAULT', extend = True, deselect = False)
2112         else:
2113             bpy.ops.view3d.select('INVOKE_DEFAULT', extend = False, deselect = True, toggle = True)
2114
2115
2116         if event.value == 'RELEASE':
2117             return {'FINISHED'}
2118
2119
2120         return {'RUNNING_MODAL'}
2121
2122     def invoke(self, context, event):
2123
2124         if self.toggle:
2125             sel_ob = len(bpy.context.selected_objects)
2126
2127             if sel_ob >= 1:
2128                 mode = bpy.context.object.mode
2129
2130                 if mode == 'EDIT':
2131                     sel_mode = bpy.context.tool_settings.mesh_select_mode[:]
2132
2133                     # Get Selection before
2134                     bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
2135                     ob = bpy.context.object.data
2136                     # check verts
2137                     if sel_mode[0]:
2138                         for v in ob.vertices:
2139                             if v.select:
2140                                 self.sel_before += 1
2141                     # check edges
2142                     elif sel_mode[1]:
2143                         for e in ob.edges:
2144                             if e.select:
2145                                 self.sel_before += 1
2146                     # check polys
2147                     else:
2148                         for p in ob.polygons:
2149                             if p.select:
2150                                 self.sel_before += 1
2151
2152                     # Toggle Selection
2153                     bpy.ops.object.mode_set(mode='EDIT', toggle=False)
2154                     bpy.ops.view3d.select('INVOKE_DEFAULT', extend = False, toggle = True)
2155
2156                     bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
2157                     ob = bpy.context.object.data
2158                     # check verts after
2159                     if sel_mode[0]:
2160                         for v in ob.vertices:
2161                             if v.select:
2162                                 self.sel_after += 1
2163
2164                     # check edges after
2165                     elif sel_mode[1]:
2166                         for e in ob.edges:
2167                             if e.select:
2168                                 self.sel_after += 1
2169                     # check polys after
2170                     else:
2171                         for p in ob.polygons:
2172                             if p.select:
2173                                 self.sel_after += 1
2174
2175
2176                     if self.sel_after > self.sel_before:
2177                         self.deselect = False
2178                     elif self.sel_after == self.sel_before:
2179                         bpy.ops.object.mode_set(mode='EDIT', toggle=False)
2180                         bpy.ops.mesh.select_all(action='DESELECT')
2181                         return {'FINISHED'}
2182                     else:
2183                         self.deselect = True
2184
2185                     bpy.ops.object.mode_set(mode='EDIT', toggle=False)
2186
2187                 elif mode == 'OBJECT':
2188                     bpy.ops.view3d.select('INVOKE_DEFAULT', extend = False, toggle = True)
2189
2190                     sel_ob_after = len(bpy.context.selected_objects)
2191
2192                     if sel_ob_after < sel_ob:
2193                         self.deselect = True
2194
2195
2196         context.window_manager.modal_handler_add(self)
2197         return {'RUNNING_MODAL'}
2198
2199
2200 class pathSelectRing(bpy.types.Operator):
2201     """Selects the shortest edge ring path"""
2202     bl_idname = "mesh.path_select_ring"
2203     bl_label = "Path Select Ring"
2204     bl_options = {'REGISTER', 'UNDO'}
2205
2206     pick: BoolProperty(name = "Pick Mode", description = "Pick Mode", default = False)
2207     collapse: BoolProperty(name = "Collapse", description = "Collapses everything between your two selected edges", default = False)
2208
2209     def draw(self, context):
2210         layout = self.layout
2211
2212
2213     def execute(self, context):
2214
2215         me = bpy.context.object.data
2216         bm = bmesh.from_edit_mesh(me)
2217         mesh = bpy.context.active_object.data
2218         sel_mode = bpy.context.tool_settings.mesh_select_mode[:]
2219
2220         org_sel = []
2221         start_end = []
2222         active_edge = []
2223         border_sel = []
2224         vert_sel = []
2225         face_sel = []
2226
2227         if sel_mode[1]:
2228
2229             bpy.context.tool_settings.mesh_select_mode = [False, True, False]
2230
2231             if self.pick:
2232                 bpy.ops.view3d.select('INVOKE_DEFAULT', extend=True, deselect=False, toggle=False)
2233
2234
2235             # Store the Start and End edges
2236             iterate = 0
2237             for e in reversed(bm.select_history):
2238                 if isinstance(e, bmesh.types.BMEdge):
2239                     iterate += 1
2240                     start_end.append(e)
2241                     if iterate >= 2:
2242                         break
2243
2244             if len(start_end) <= 1:
2245                 if self.collapse:
2246                     bpy.ops.mesh.merge(type='COLLAPSE', uvs=True)
2247                     return{'FINISHED'}
2248                 return{'CANCELLED'}
2249
2250             # Store active edge
2251             for e in reversed(bm.select_history):
2252                 if isinstance(e, bmesh.types.BMEdge):
2253                     active_edge = e.index
2254                     break
2255
2256             # Store original edges
2257             for e in bm.edges:
2258                 if e.select:
2259                     org_sel.append(e)
2260
2261             # Store visible faces
2262             bpy.ops.mesh.select_all(action='SELECT')
2263             for f in bm.faces:
2264                 if f.select:
2265                     face_sel.append(f)
2266
2267
2268             # Store boundry edges
2269             bpy.ops.mesh.region_to_loop()
2270
2271             for e in bm.edges:
2272                 if e.select:
2273                     border_sel.append(e)
2274
2275             bpy.ops.mesh.select_all(action='DESELECT')
2276
2277             # Select Start and End edges
2278             for e in start_end:
2279                 e.select = True
2280
2281             # Hide trick
2282             bpy.ops.mesh.loop_multi_select(ring=True)
2283
2284             bpy.ops.mesh.select_mode(use_extend=False, use_expand=True, type='FACE')
2285             bpy.ops.mesh.hide(unselected=True)
2286
2287             bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
2288             bpy.ops.mesh.select_all(action='DESELECT')
2289             for e in start_end:
2290                 e.select = True
2291             bpy.ops.mesh.shortest_path_select()
2292             bpy.ops.mesh.select_more()
2293             bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')
2294
2295             bpy.ops.mesh.select_all(action='INVERT')
2296             bpy.ops.mesh.reveal()
2297             bpy.ops.mesh.select_all(action='INVERT')
2298             bpy.ops.mesh.select_mode(use_extend=False, use_expand=True, type='EDGE')
2299
2300             # Deselect border edges
2301             for e in border_sel:
2302                 e.select = False
2303
2304             # Add to original selection
2305             for e in bm.edges:
2306                 if e.select:
2307                     org_sel.append(e)
2308
2309             # Restore hidden polygons
2310             bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')
2311             for f in face_sel:
2312                 f.select = True
2313             bpy.ops.mesh.hide(unselected=True)
2314
2315
2316             # Reselect original selection
2317             bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
2318             bpy.ops.mesh.select_all(action='DESELECT')
2319             for e in org_sel:
2320                 e.select = True
2321
2322             # Set active edge
2323             bm.select_history.add(bm.edges[active_edge])
2324
2325
2326             if self.collapse:
2327                 bpy.ops.mesh.merge(type='COLLAPSE', uvs=True)
2328
2329             bmesh.update_edit_mesh(me, True, False)
2330
2331             return {'FINISHED'}
2332
2333         else:
2334             self.report({'WARNING'}, "This tool only workins in edge mode.")
2335             return {'CANCELLED'}
2336
2337
2338
2339
2340 #Draws the Custom Menu in Object Mode
2341 class ktools_menu(bpy.types.Menu):
2342         bl_label = "KTools - Object Mode"
2343         bl_idname = "OBJECT_MT_ktools_menu"
2344
2345         def draw(self, context):
2346
2347                 layout = self.layout
2348                 layout.operator_context = 'INVOKE_DEFAULT'
2349                 layout.operator("mesh.draw_poly")
2350                 layout.operator("object.toggle_silhouette")
2351                 layout.operator("mesh.quickbool")
2352                 layout.operator("mesh.calc_normals")
2353                 layout.operator("object.custom_autosmooth")
2354                 layout.operator("object.basic_rename")
2355                 layout.operator("object.lattice_to_selection")
2356
2357
2358 #Draws the Custom Menu in Edit Mode
2359 class VIEW3D_MT_edit_mesh_ktools_menuEdit(bpy.types.Menu):
2360         bl_label = "KTools - Edit Mode"
2361         bl_idname = "VIEW3D_MT_edit_mesh_ktools_menuEdit"
2362
2363         def draw(self, context):
2364
2365
2366
2367                 layout = self.layout
2368
2369                 layout.operator("mesh.cut_tool")
2370                 layout.operator("mesh.snaptoaxis")
2371                 layout.operator("mesh.autotubes")
2372                 layout.operator("mesh.shrinkwrap_smooth")
2373
2374                 layout.operator_context = 'INVOKE_DEFAULT'
2375                 layout.operator("mesh.build_corner")
2376                 layout.operator("object.lattice_to_selection")
2377
2378                 layout.separator()
2379
2380                 layout.operator("mesh.path_select_ring")
2381                 layout.operator("mesh.grow_loop")
2382                 layout.operator("mesh.shrink_loop")
2383                 layout.operator("mesh.extend_loop")
2384
2385                 layout.separator()
2386
2387                 layout.operator("mesh.draw_poly")
2388                 layout.operator("object.toggle_silhouette")
2389                 layout.operator("mesh.quickbool")
2390                 layout.operator("object.custom_autosmooth")
2391                 layout.operator("mesh.calc_normals")
2392                 layout.operator("object.basic_rename")
2393
2394
2395
2396 #Calls the KTools Object Menu
2397 class ktools(bpy.types.Operator): #Namesuggestion: K-Tools or K-Mac
2398         """Calls the KTools Menu"""
2399         bl_idname = "object.ktools"
2400         bl_label = "KTools Object Menu"
2401         #bl_options = {'REGISTER', 'UNDO'}
2402
2403         def execute(self, context):
2404
2405
2406             bpy.ops.wm.call_menu(name=ktools_menu.bl_idname)
2407             return {'FINISHED'}
2408
2409             """
2410             sel_ob = bpy.context.object
2411
2412
2413             if sel_ob:
2414
2415                 mode = bpy.context.active_object.mode
2416
2417                 if mode == 'EDIT':
2418                         bpy.ops.wm.call_menu(name=VIEW3D_MT_edit_mesh_ktools_menuEdit.bl_idname)
2419
2420                 else:
2421                         bpy.ops.wm.call_menu(name=ktools_menu.bl_idname)
2422
2423                 return {'FINISHED'}
2424
2425             else:
2426                 bpy.ops.wm.call_menu(name=ktools_menu.bl_idname)
2427                 return {'FINISHED'}
2428                 #self.report({'WARNING'}, "Active object is not a mesh.")
2429                 #return {'CANCELLED'}
2430                 """
2431
2432 #Calls the KTools Edit Menu
2433 class ktools_mesh(bpy.types.Operator): #Namesuggestion: K-Tools or K-Mac
2434         """Calls the KTools Edit Menu"""
2435         bl_idname = "mesh.ktools_mesh"
2436         bl_label = "KTools Mesh Menu"
2437         #bl_options = {'REGISTER', 'UNDO'}
2438
2439         def execute(self, context):
2440
2441
2442             bpy.ops.wm.call_menu(name=VIEW3D_MT_edit_mesh_ktools_menuEdit.bl_idname)
2443             return {'FINISHED'}
2444
2445
2446 # draw function for integration in menus
2447 def menu_func(self, context):
2448     self.layout.separator()
2449     self.layout.menu("VIEW3D_MT_edit_mesh_ktools_menuEdit", text = "KTools")
2450 def menu_func_ob(self, context):
2451     self.layout.separator()
2452     self.layout.menu("OBJECT_MT_ktools_menu", text = "KTools")
2453
2454 #Register and Unregister all the operators
2455 def register():
2456         bpy.utils.register_class(lattice_to_selection)
2457         bpy.utils.register_class(calc_normals)
2458         bpy.utils.register_class(snaptoaxis)
2459         bpy.utils.register_class(quickbool)
2460         bpy.utils.register_class(autotubes)
2461         bpy.utils.register_class(basicRename)
2462         bpy.utils.register_class(cut_tool)
2463         bpy.utils.register_class(customAutoSmooth)
2464         bpy.utils.register_class(shrinkwrapSmooth)
2465         bpy.utils.register_class(buildCorner)
2466         bpy.utils.register_class(drawPoly)
2467         bpy.utils.register_class(toggleSilhouette)
2468         bpy.utils.register_class(growLoop)
2469         bpy.utils.register_class(extendLoop)
2470         bpy.utils.register_class(shrinkLoop)
2471         bpy.utils.register_class(paintSelect)
2472         bpy.utils.register_class(pathSelectRing)
2473         bpy.utils.register_class(ktools_menu)
2474         bpy.utils.register_class(VIEW3D_MT_edit_mesh_ktools_menuEdit)
2475         bpy.utils.register_class(ktools)
2476         bpy.utils.register_class(ktools_mesh)
2477
2478         bpy.types.VIEW3D_MT_edit_mesh_specials.prepend(menu_func)
2479         bpy.types.VIEW3D_MT_object_specials.prepend(menu_func_ob)
2480
2481         kc = bpy.context.window_manager.keyconfigs.addon
2482         if kc:
2483             # Add paint select to CTRL+SHIFT+ALT+LeftMouse
2484             km = kc.keymaps.new(name="3D View", space_type="VIEW_3D")
2485             kmi = km.keymap_items.new('view3d.select_paint', 'LEFTMOUSE', 'PRESS', shift=True, ctrl=True, alt=True)
2486
2487
2488
2489 def unregister():
2490         bpy.utils.unregister_class(lattice_to_selection)
2491         bpy.utils.unregister_class(calc_normals)
2492         bpy.utils.unregister_class(snaptoaxis)
2493         bpy.utils.unregister_class(quickbool)
2494         bpy.utils.unregister_class(autotubes)
2495         bpy.utils.unregister_class(basicRename)
2496         bpy.utils.unregister_class(cut_tool)
2497         bpy.utils.unregister_class(customAutoSmooth)
2498         bpy.utils.unregister_class(shrinkwrapSmooth)
2499         bpy.utils.unregister_class(buildCorner)
2500         bpy.utils.unregister_class(drawPoly)
2501         bpy.utils.unregister_class(toggleSilhouette)
2502         bpy.utils.unregister_class(growLoop)
2503         bpy.utils.unregister_class(extendLoop)
2504         bpy.utils.unregister_class(shrinkLoop)
2505         bpy.utils.unregister_class(paintSelect)
2506         bpy.utils.unregister_class(pathSelectRing)
2507         bpy.utils.unregister_class(ktools_menu)
2508         bpy.utils.unregister_class(VIEW3D_MT_edit_mesh_ktools_menuEdit)
2509         bpy.utils.unregister_class(ktools)
2510         bpy.utils.unregister_class(ktools_mesh)
2511
2512         bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_func)
2513         bpy.types.VIEW3D_MT_object_specials.remove(menu_func_ob)
2514
2515         kc = bpy.context.window_manager.keyconfigs.addon
2516         if kc:
2517             km = kc.keymaps["3D View"]
2518             for kmi in km.keymap_items:
2519                 if kmi.idname == 'view3d.select_paint':
2520                     km.keymap_items.remove(kmi)
2521                     break
2522
2523
2524
2525 # This allows you to run the script directly from blenders text editor
2526 # to test the addon without having to install it.
2527 if __name__ == "__main__":
2528         register()