CM3D2 Converter.misc_MESH_MT_vertex_group_specials

  1# 「プロパティ」エリア → 「メッシュデータ」タブ → 「頂点グループ」パネル → ▼ボタン
  2import time
  3import bpy
  4import bmesh
  5import mathutils
  6from . import common
  7from . import compat
  8from .translations.pgettext_functions import *
  9
 10
 11# メニュー等に項目追加
 12def menu_func(self, context):
 13    icon_id = common.kiss_icon()
 14    self.layout.separator()
 15    self.layout.operator('object.quick_transfer_vertex_group', icon_value=icon_id)
 16    self.layout.operator('object.precision_transfer_vertex_group', icon_value=icon_id)
 17    self.layout.separator()
 18    self.layout.operator('object.quick_blur_vertex_group', icon_value=icon_id)
 19    self.layout.operator('object.blur_vertex_group', icon_value=icon_id)
 20    self.layout.separator()
 21    self.layout.operator('object.multiply_vertex_group', icon_value=icon_id)
 22    self.layout.separator()
 23    self.layout.operator('object.remove_noassign_vertex_groups', icon_value=icon_id)
 24
 25
 26@compat.BlRegister()
 27class CNV_OT_quick_transfer_vertex_group(bpy.types.Operator):
 28    bl_idname = 'object.quick_transfer_vertex_group'
 29    bl_label = "クイック・ウェイト転送"
 30    bl_description = "アクティブなメッシュに他の選択メッシュの頂点グループを高速で転送します"
 31    bl_options = {'REGISTER', 'UNDO'}
 32
 33    is_remove_old_vertex_groups = bpy.props.BoolProperty(name="すでにある頂点グループを削除 (ロックで保護)", default=False)
 34    is_source_select_vert_only = bpy.props.BoolProperty(name="選択頂点のみ(参照)", default=False)
 35    is_target_select_vert_only = bpy.props.BoolProperty(name="選択頂点のみ(対象)", default=False)
 36    items = [
 37        ('NEAREST', "最も近い頂点", "", 'VERTEXSEL', 1),
 38        ('EDGEINTERP_NEAREST', "最も近い辺", "", 'EDGESEL', 2),
 39        ('POLYINTERP_NEAREST', "最も近い面", "", 'FACESEL', 3),
 40        ('POLYINTERP_VNORPROJ', "投影先", "", 'MOD_UVPROJECT', 4),
 41    ]
 42    vert_mapping = bpy.props.EnumProperty(items=items, name="参照要素", default='POLYINTERP_NEAREST')
 43    is_clean = bpy.props.BoolProperty(name="転送後にクリーンを実行", default=True)
 44    is_remove_noassign = bpy.props.BoolProperty(name="転送後に割り当てのない頂点グループを削除", default=True)
 45
 46    @classmethod
 47    def poll(cls, context):
 48        obs = context.selected_objects
 49        if len(obs) < 2:
 50            return False
 51
 52        active_ob = context.active_object
 53        for ob in obs:
 54            if ob.type != 'MESH':
 55                return False
 56            if ob.name != active_ob.name:
 57                if not len(ob.vertex_groups):
 58                    return False
 59        return True
 60
 61    def invoke(self, context, event):
 62        return context.window_manager.invoke_props_dialog(self)
 63
 64    def draw(self, context):
 65        self.layout.prop(self, 'is_remove_old_vertex_groups', icon='ERROR')
 66
 67        row = self.layout.row(align=True)
 68        row.prop(self, 'is_source_select_vert_only', icon='UV_SYNC_SELECT')
 69        row.prop(self, 'is_target_select_vert_only', icon='UV_SYNC_SELECT')
 70
 71        self.layout.prop(self, 'vert_mapping')
 72        self.layout.prop(self, 'is_clean', icon='DISCLOSURE_TRI_DOWN')
 73        self.layout.prop(self, 'is_remove_noassign', icon='X')
 74
 75    def execute(self, context):
 76        class MyVertexGroupElement:
 77            pass
 78
 79        target_ob = context.active_object
 80        target_me = target_ob.data
 81
 82        pre_mode = target_ob.mode
 83        bpy.ops.object.mode_set(mode='OBJECT')
 84
 85        original_source_obs = []
 86        for ob in context.selected_objects:
 87            if ob.name != target_ob.name:
 88                original_source_obs.append(ob)
 89
 90        compat.set_select(target_ob, False)
 91        compat.set_active(context, original_source_obs[0])
 92        bpy.ops.object.duplicate(linked=False, mode='TRANSLATION')
 93        if len(context.selected_objects) > 1:
 94            bpy.ops.object.join()
 95        join_source_ob = context.selected_objects[0]
 96        join_source_me = join_source_ob.data
 97
 98        temp_source_ob = join_source_ob.copy()
 99        temp_source_me = join_source_me.copy()
100        temp_source_ob.data = temp_source_me
101        compat.link(context.scene, temp_source_ob)
102        compat.set_select(temp_source_ob, True)
103        compat.set_select(join_source_ob, False)
104        compat.set_active(context, temp_source_ob)
105        if self.is_source_select_vert_only:
106            bpy.ops.object.mode_set(mode='EDIT')
107            bpy.ops.mesh.select_all(action='INVERT')
108            bpy.ops.mesh.delete(type='VERT')
109            bpy.ops.object.mode_set(mode='OBJECT')
110
111        compat.set_select(target_ob, True)
112        compat.set_active(context, target_ob)
113
114        if self.vert_mapping == 'POLYINTERP_VNORPROJ' and len(temp_source_me.polygons) == 0:
115            self.vert_mapping = 'EDGEINTERP_NEAREST'
116            self.report(type={'WARNING'}, message="面がひとつも存在しません、辺モードに変更します")
117        if self.vert_mapping == 'POLYINTERP_NEAREST' and len(temp_source_me.polygons) == 0:
118            self.vert_mapping = 'EDGEINTERP_NEAREST'
119            self.report(type={'WARNING'}, message="面がひとつも存在しません、辺モードに変更します")
120        if self.vert_mapping == 'EDGEINTERP_NEAREST' and len(temp_source_me.edges) == 0:
121            self.vert_mapping = 'NEAREST'
122            self.report(type={'WARNING'}, message="辺がひとつも存在しません、頂点モードに変更します")
123        if self.vert_mapping == 'NEAREST' and len(temp_source_me.vertices) == 0:
124            self.report(type={'ERROR'}, message="頂点がひとつも存在しません、中止します")
125            return {'CANCELLED'}
126
127        if self.is_remove_old_vertex_groups:
128            for vg in target_ob.vertex_groups[:]:
129                if not vg.lock_weight:
130                    target_ob.vertex_groups.remove(vg)
131
132        old_vertex_groups = []
133        for vert in target_me.vertices:
134            mvges = []
135            for vge in vert.groups:
136                mvge = MyVertexGroupElement()
137                mvge.vertex_group = target_ob.vertex_groups[vge.group]
138                mvge.weight = vge.weight
139                mvges.append(mvge)
140            old_vertex_groups.append(mvges)
141
142        if self.is_remove_noassign:
143            pre_vertex_group_names = [vg.name for vg in target_ob.vertex_groups]
144
145        bpy.ops.object.data_transfer(use_reverse_transfer=True, use_freeze=False, data_type='VGROUP_WEIGHTS', use_create=True, vert_mapping=self.vert_mapping, use_auto_transform=False, use_object_transform=True, use_max_distance=False, ray_radius=0, layers_select_src='NAME', layers_select_dst='ALL', mix_mode='REPLACE', mix_factor=1)
146        if self.is_clean:
147            bpy.ops.object.vertex_group_clean(group_select_mode='ALL', limit=0.00000000001)
148
149        bpy.ops.object.mode_set(mode='EDIT')
150        bpy.ops.object.mode_set(mode='OBJECT')
151
152        if self.is_remove_noassign:
153            is_keeps = [False for i in range(len(target_ob.vertex_groups))]
154            for vert in target_me.vertices:
155                for vge in vert.groups:
156                    if not is_keeps[vge.group]:
157                        if 0.000001 < vge.weight:
158                            is_keeps[vge.group] = True
159            copy_vertex_groups = target_ob.vertex_groups[:]
160            for i in range(len(copy_vertex_groups)):
161                if not is_keeps[i] and not copy_vertex_groups[i].lock_weight:
162                    if copy_vertex_groups[i].name not in pre_vertex_group_names:
163                        target_ob.vertex_groups.remove(copy_vertex_groups[i])
164
165        if self.is_target_select_vert_only:
166            for vert in target_me.vertices:
167                if not vert.select:
168                    for vg in target_ob.vertex_groups:
169                        vg.remove([vert.index])
170                    for mvge in old_vertex_groups[vert.index]:
171                        mvge.vertex_group.add([vert.index], mvge.weight, 'REPLACE')
172
173        for vert in target_me.vertices:
174            for vg in target_ob.vertex_groups:
175                if vg.lock_weight:
176                    vg.remove([vert.index])
177            for mvge in old_vertex_groups[vert.index]:
178                if mvge.vertex_group.lock_weight:
179                    mvge.vertex_group.add([vert.index], mvge.weight, 'REPLACE')
180
181        common.remove_data([temp_source_ob, temp_source_me])
182        compat.set_select(join_source_ob, True)
183
184        common.remove_data([join_source_ob, join_source_me])
185        for ob in original_source_obs:
186            compat.set_select(ob, True)
187
188        compat.set_active(context, target_ob)
189        bpy.ops.object.mode_set(mode=pre_mode)
190        return {'FINISHED'}
191
192
193@compat.BlRegister()
194class CNV_OT_precision_transfer_vertex_group(bpy.types.Operator):
195    bl_idname = 'object.precision_transfer_vertex_group'
196    bl_label = "空間ぼかし・ウェイト転送"
197    bl_description = "アクティブなメッシュに他の選択メッシュの頂点グループを遠いほどぼかして転送します"
198    bl_options = {'REGISTER', 'UNDO'}
199
200    is_first_remove_all = bpy.props.BoolProperty(name="すでにある頂点グループを削除 (ロックで保護)", default=False)
201    subdivide_number = bpy.props.IntProperty(name="参照元の分割", default=1, min=0, max=10, soft_min=0, soft_max=10)
202    extend_range = bpy.props.FloatProperty(name="範囲倍率", default=1.1, min=1.0001, max=5.0, soft_min=1.0001, soft_max=5.0, step=10, precision=2)
203    is_remove_empty = bpy.props.BoolProperty(name="割り当てのない頂点グループを削除", default=True)
204
205    @classmethod
206    def poll(cls, context):
207        active_ob = context.active_object
208        obs = context.selected_objects
209        if len(obs) != 2:
210            return False
211
212        for ob in obs:
213            if ob.type != 'MESH':
214                return False
215            if ob.name != active_ob.name:
216                if len(ob.vertex_groups):
217                    return True
218        return False
219
220    def invoke(self, context, event):
221        return context.window_manager.invoke_props_dialog(self)
222
223    def draw(self, context):
224        self.layout.prop(self, 'is_first_remove_all', icon='ERROR')
225        self.layout.prop(self, 'subdivide_number', icon='LATTICE_DATA')
226        self.layout.prop(self, 'extend_range', icon='PROP_ON')
227        self.layout.prop(self, 'is_remove_empty', icon='X')
228
229    def execute(self, context):
230        start_time = time.time()
231
232        target_ob = context.active_object
233        target_me = target_ob.data
234
235        pre_mode = target_ob.mode
236        bpy.ops.object.mode_set(mode='OBJECT')
237
238        for ob in context.selected_objects:
239            if ob.name != target_ob.name:
240                source_original_ob = ob
241                break
242        source_ob = source_original_ob.copy()
243        source_me = source_original_ob.data.copy()
244        source_ob.data = source_me
245        compat.link(context.scene, source_ob)
246
247        compat.set_select(target_ob, False)
248        compat.set_select(source_original_ob, False)
249        compat.set_active(context, source_ob)
250        try:
251            bpy.ops.object.mode_set(mode='EDIT')
252            bpy.ops.mesh.reveal()
253            bpy.ops.mesh.select_all(action='SELECT')
254            bpy.ops.mesh.subdivide(number_cuts=self.subdivide_number, smoothness=0.0, quadcorner='STRAIGHT_CUT', fractal=0.0, fractal_along_normal=0.0, seed=0)
255            bpy.ops.object.mode_set(mode='OBJECT')
256
257            if self.is_first_remove_all:
258                for vg in target_ob.vertex_groups[:]:
259                    if not vg.lock_weight:
260                        target_ob.vertex_groups.remove(vg)
261
262            kd = mathutils.kdtree.KDTree(len(source_me.vertices))
263            for vert in source_me.vertices:
264                co = compat.mul(source_ob.matrix_world, vert.co)
265                kd.insert(co, vert.index)
266            kd.balance()
267
268            context.window_manager.progress_begin(0, len(target_me.vertices))
269            progress_reduce = len(target_me.vertices) // 200 + 1
270            near_vert_data = []
271            near_vert_multi_total = []
272            near_vert_multi_total_append = near_vert_multi_total.append
273            for vert in target_me.vertices:
274                near_vert_data.append([])
275                near_vert_data_append = near_vert_data[-1].append
276
277                target_co = compat.mul(target_ob.matrix_world, vert.co)
278
279                mini_co, mini_index, mini_dist = kd.find(target_co)
280                radius = mini_dist * self.extend_range
281                diff_radius = radius - mini_dist
282
283                multi_total = 0.0
284                for co, index, dist in kd.find_range(target_co, radius):
285                    if 0 < diff_radius:
286                        multi = (diff_radius - (dist - mini_dist)) / diff_radius
287                    else:
288                        multi = 1.0
289                    near_vert_data_append((index, multi))
290                    multi_total += multi
291                near_vert_multi_total_append(multi_total)
292
293                if vert.index % progress_reduce == 0:
294                    context.window_manager.progress_update(vert.index)
295            context.window_manager.progress_end()
296
297            context.window_manager.progress_begin(0, len(source_ob.vertex_groups))
298            for source_vertex_group in source_ob.vertex_groups:
299
300                if source_vertex_group.name in target_ob.vertex_groups:
301                    target_vertex_group = target_ob.vertex_groups[source_vertex_group.name]
302                else:
303                    target_vertex_group = target_ob.vertex_groups.new(name=source_vertex_group.name)
304
305                is_waighted = False
306
307                source_weights = []
308                source_weights_append = source_weights.append
309                for source_vert in source_me.vertices:
310                    for elem in source_vert.groups:
311                        if elem.group == source_vertex_group.index:
312                            source_weights_append(elem.weight)
313                            break
314                    else:
315                        source_weights_append(0.0)
316
317                for target_vert in target_me.vertices:
318
319                    if 0 < near_vert_multi_total[target_vert.index]:
320
321                        total_weight = [source_weights[i] * m for i, m in near_vert_data[target_vert.index]]
322                        total_weight = sum(total_weight)
323
324                        average_weight = total_weight / near_vert_multi_total[target_vert.index]
325                    else:
326                        average_weight = 0.0
327
328                    if 0.000001 < average_weight:
329                        target_vertex_group.add([target_vert.index], average_weight, 'REPLACE')
330                        is_waighted = True
331                    else:
332                        if not self.is_first_remove_all:
333                            target_vertex_group.remove([target_vert.index])
334
335                context.window_manager.progress_update(source_vertex_group.index)
336
337                if not is_waighted and self.is_remove_empty:
338                    target_ob.vertex_groups.remove(target_vertex_group)
339            context.window_manager.progress_end()
340
341            target_ob.vertex_groups.active_index = 0
342        finally:
343            common.remove_data([source_ob, source_me])
344            compat.set_select(source_original_ob, True)
345            compat.set_select(target_ob, True)
346            compat.set_active(context, target_ob)
347            bpy.ops.object.mode_set(mode=pre_mode)
348
349        diff_time = time.time() - start_time
350        self.report(type={'INFO'}, message=f_tip_("{:.2f} Seconds", diff_time))
351        return {'FINISHED'}
352
353
354@compat.BlRegister()
355class CNV_OT_quick_blur_vertex_group(bpy.types.Operator):
356    bl_idname = 'object.quick_blur_vertex_group'
357    bl_label = "頂点グループぼかし"
358    bl_description = "アクティブ、もしくは全ての頂点グループをぼかします"
359    bl_options = {'REGISTER', 'UNDO'}
360
361    items = [
362        ('ACTIVE', "アクティブのみ", "", 'HAND', 1),
363        ('ALL', "全て", "", 'ARROW_LEFTRIGHT', 2),
364    ]
365    target = bpy.props.EnumProperty(items=items, name="対象", default='ALL')
366    strength = bpy.props.FloatProperty(name="強さ", default=1.0, min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, step=10, precision=3)
367    count = bpy.props.IntProperty(name="反復", default=1, min=1, max=256, soft_min=1, soft_max=256)
368    size = bpy.props.FloatProperty(name="拡大縮小", default=0.0, min=-1.0, max=1.0, soft_min=-1.0, soft_max=1.0, step=10, precision=3)
369
370    @classmethod
371    def poll(cls, context):
372        ob = context.active_object
373        if ob and ob.type == 'MESH':
374            return ob.vertex_groups.active
375        return False
376
377    def invoke(self, context, event):
378        return context.window_manager.invoke_props_dialog(self)
379
380    def draw(self, context):
381        self.layout.prop(self, 'target', icon='VIEWZOOM')
382        self.layout.prop(self, 'strength')
383        self.layout.prop(self, 'count')
384        self.layout.prop(self, 'size')
385
386    def execute(self, context):
387        target_ob = context.active_object
388        target_me = target_ob.data
389
390        pre_mode = target_ob.mode
391        bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
392
393        pre_use_paint_mask_vertex = target_me.use_paint_mask_vertex
394        target_me.use_paint_mask_vertex = True
395
396        bpy.ops.paint.vert_select_all(action='SELECT')
397        # source='ALL'は2.8で削除された
398        bpy.ops.object.vertex_group_smooth(group_select_mode=self.target, factor=self.strength, repeat=self.count, expand=self.size)
399
400        target_me.use_paint_mask_vertex = pre_use_paint_mask_vertex
401
402        bpy.ops.object.mode_set(mode=pre_mode)
403        return {'FINISHED'}
404
405
406@compat.BlRegister()
407class CNV_OT_blur_vertex_group(bpy.types.Operator):
408    bl_idname = 'object.blur_vertex_group'
409    bl_label = "旧・頂点グループぼかし"
410    bl_description = "アクティブ、もしくは全ての頂点グループをぼかします"
411    bl_options = {'REGISTER', 'UNDO'}
412
413    items = [
414        ('ACTIVE', "アクティブのみ", "", 'HAND', 1),
415        ('UP', "アクティブより上", "", 'TRIA_UP_BAR', 2),
416        ('DOWN', "アクティブより下", "", 'TRIA_DOWN_BAR', 3),
417        ('ALL', "全て", "", 'ARROW_LEFTRIGHT', 4),
418    ]
419    target = bpy.props.EnumProperty(items=items, name="対象", default='ACTIVE')
420    radius = bpy.props.FloatProperty(name="範囲倍率", default=3, min=0.1, max=50, soft_min=0.1, soft_max=50, step=50, precision=2)
421    strength = bpy.props.IntProperty(name="強さ", default=1, min=1, max=10, soft_min=1, soft_max=10)
422    items = [
423        ('BOTH', "増減両方", "", 'AUTOMERGE_ON', 1),
424        ('ADD', "増加のみ", "", 'TRIA_UP', 2),
425        ('SUB', "減少のみ", "", 'TRIA_DOWN', 3),
426    ]
427    effect = bpy.props.EnumProperty(items=items, name="ぼかし効果", default='BOTH')
428    is_normalize = bpy.props.BoolProperty(name="他頂点グループも調節", default=True)
429
430    @classmethod
431    def poll(cls, context):
432        ob = context.active_object
433        if ob and ob.type == 'MESH':
434            return ob.vertex_groups.active
435        return False
436
437    def invoke(self, context, event):
438        return context.window_manager.invoke_props_dialog(self)
439
440    def draw(self, context):
441        self.layout.prop(self, 'target', icon='VIEWZOOM')
442        self.layout.prop(self, 'radius', icon='PROP_ON')
443        self.layout.prop(self, 'strength', icon='ARROW_LEFTRIGHT')
444        self.layout.prop(self, 'effect', icon='BRUSH_BLUR')
445        self.layout.prop(self, 'is_normalize', icon='GROUP')
446
447    def execute(self, context):
448        ob = context.active_object
449        me = ob.data
450
451        pre_mode = ob.mode
452        bpy.ops.object.mode_set(mode='OBJECT')
453
454        bm = bmesh.new()
455        bm.from_mesh(me)
456        edge_lengths = [e.calc_length() for e in bm.edges]
457        bm.free()
458        edge_lengths.sort()
459        average_edge_length = sum(edge_lengths) / len(edge_lengths)
460        center_index = int((len(edge_lengths) - 1) / 2.0)
461        average_edge_length = (average_edge_length + edge_lengths[center_index]) / 2
462        radius = average_edge_length * self.radius
463
464        context.window_manager.progress_begin(0, len(me.vertices))
465        progress_reduce = len(me.vertices) // 200 + 1
466        near_vert_data = []
467        kd = mathutils.kdtree.KDTree(len(me.vertices))
468        for vert in me.vertices:
469            kd.insert(vert.co.copy(), vert.index)
470        kd.balance()
471        for vert in me.vertices:
472            new_vert_datum = []
473            near_vert_data.append(new_vert_datum)
474            near_vert_data_append = new_vert_datum.append
475            for co, index, dist in kd.find_range(vert.co, radius):
476                multi = (radius - dist) / radius
477                near_vert_data_append((index, multi))
478            if vert.index % progress_reduce == 0:
479                context.window_manager.progress_update(vert.index)
480        context.window_manager.progress_end()
481
482        target_vertex_groups = []
483        if self.target == 'ACTIVE':
484            target_vertex_groups.append(ob.vertex_groups.active)
485        elif self.target == 'UP':
486            for vertex_group in ob.vertex_groups:
487                if vertex_group.index <= ob.vertex_groups.active_index:
488                    target_vertex_groups.append(vertex_group)
489        elif self.target == 'DOWN':
490            for vertex_group in ob.vertex_groups:
491                if ob.vertex_groups.active_index <= vertex_group.index:
492                    target_vertex_groups.append(vertex_group)
493        elif self.target == 'ALL':
494            for vertex_group in ob.vertex_groups:
495                target_vertex_groups.append(vertex_group)
496
497        progress_total = len(target_vertex_groups) * self.strength * len(me.vertices)
498        context.window_manager.progress_begin(0, progress_total)
499        progress_reduce = progress_total // 200 + 1
500        progress_count = 0
501        for strength_count in range(self.strength):
502            for vertex_group in target_vertex_groups:
503
504                weights = []
505                weights_append = weights.append
506                for vert in me.vertices:
507                    for elem in vert.groups:
508                        if elem.group == vertex_group.index:
509                            weights_append(elem.weight)
510                            break
511                    else:
512                        weights_append(0.0)
513
514                for vert in me.vertices:
515
516                    target_weight = weights[vert.index]
517
518                    total_weight = 0.0
519                    total_multi = 0.0
520                    for index, multi in near_vert_data[vert.index]:
521                        if self.effect == 'ADD':
522                            if target_weight <= weights[index]:
523                                total_weight += weights[index] * multi
524                                total_multi += multi
525                        elif self.effect == 'SUB':
526                            if weights[index] <= target_weight:
527                                total_weight += weights[index] * multi
528                                total_multi += multi
529                        else:
530                            total_weight += weights[index] * multi
531                            total_multi += multi
532
533                    if 0 < total_multi:
534                        average_weight = total_weight / total_multi
535                    else:
536                        average_weight = 0.0
537
538                    if 0.001 < average_weight:
539                        vertex_group.add([vert.index], average_weight, 'REPLACE')
540                    else:
541                        vertex_group.remove([vert.index])
542
543                    progress_count += 1
544                    if progress_count % progress_reduce == 0:
545                        context.window_manager.progress_update(progress_count)
546
547                    if self.is_normalize:
548
549                        other_weight_total = 0.0
550                        for elem in vert.groups:
551                            if elem.group != vertex_group.index:
552                                other_weight_total += elem.weight
553
554                        diff_weight = average_weight - target_weight
555                        new_other_weight_total = other_weight_total - diff_weight
556                        if 0 < other_weight_total:
557                            other_weight_multi = new_other_weight_total / other_weight_total
558                        else:
559                            other_weight_multi = 0.0
560
561                        for elem in vert.groups:
562                            if elem.group != vertex_group.index:
563                                vg = ob.vertex_groups[elem.group]
564                                vg.add([vert.index], elem.weight * other_weight_multi, 'REPLACE')
565
566        context.window_manager.progress_end()
567        bpy.ops.object.mode_set(mode=pre_mode)
568        return {'FINISHED'}
569
570
571@compat.BlRegister()
572class CNV_OT_multiply_vertex_group(bpy.types.Operator):
573    bl_idname = 'object.multiply_vertex_group'
574    bl_label = "頂点グループに乗算"
575    bl_description = "頂点グループのウェイトに数値を乗算し、ウェイトの強度を増減させます"
576    bl_options = {'REGISTER', 'UNDO'}
577
578    items = [
579        ('ACTIVE', "アクティブのみ", "", 'HAND', 1),
580        ('UP', "アクティブより上", "", 'TRIA_UP_BAR', 2),
581        ('DOWN', "アクティブより下", "", 'TRIA_DOWN_BAR', 3),
582        ('ALL', "全て", "", 'ARROW_LEFTRIGHT', 4),
583    ]
584    target = bpy.props.EnumProperty(items=items, name="対象", default='ACTIVE')
585    value = bpy.props.FloatProperty(name="倍率", default=1.1, min=0.1, max=10, soft_min=0.1, soft_max=10, step=10, precision=2)
586    is_normalize = bpy.props.BoolProperty(name="他頂点グループも調節", default=True)
587
588    @classmethod
589    def poll(cls, context):
590        ob = context.active_object
591        if ob:
592            if ob.type == 'MESH':
593                return ob.vertex_groups.active
594        return False
595
596    def invoke(self, context, event):
597        return context.window_manager.invoke_props_dialog(self)
598
599    def draw(self, context):
600        self.layout.prop(self, 'target', icon='VIEWZOOM')
601        self.layout.prop(self, 'value', icon='ARROW_LEFTRIGHT')
602        self.layout.prop(self, 'is_normalize', icon='GROUP')
603
604    def execute(self, context):
605        ob = context.active_object
606        me = ob.data
607
608        pre_mode = ob.mode
609        bpy.ops.object.mode_set(mode='OBJECT')
610
611        target_vertex_groups = []
612        if self.target == 'ACTIVE':
613            target_vertex_groups.append(ob.vertex_groups.active)
614        elif self.target == 'UP':
615            for vertex_group in ob.vertex_groups:
616                if vertex_group.index <= ob.vertex_groups.active_index:
617                    target_vertex_groups.append(vertex_group)
618        elif self.target == 'DOWN':
619            for vertex_group in ob.vertex_groups:
620                if ob.vertex_groups.active_index <= vertex_group.index:
621                    target_vertex_groups.append(vertex_group)
622        elif self.target == 'ALL':
623            for vertex_group in ob.vertex_groups:
624                target_vertex_groups.append(vertex_group)
625
626        for vertex_group in target_vertex_groups:
627            for vert in me.vertices:
628
629                old_weight = -1
630                other_weight_total = 0.0
631                for elem in vert.groups:
632                    if elem.group == vertex_group.index:
633                        old_weight = elem.weight
634                    else:
635                        other_weight_total += elem.weight
636                if old_weight == -1:
637                    continue
638
639                new_weight = old_weight * self.value
640                vertex_group.add([vert.index], new_weight, 'REPLACE')
641
642                if self.is_normalize:
643
644                    diff_weight = new_weight - old_weight
645
646                    new_other_weight_total = other_weight_total - diff_weight
647                    if 0 < other_weight_total:
648                        other_weight_multi = new_other_weight_total / other_weight_total
649                    else:
650                        other_weight_multi = 0.0
651
652                    for elem in vert.groups:
653                        if elem.group != vertex_group.index:
654                            vg = ob.vertex_groups[elem.group]
655                            vg.add([vert.index], elem.weight * other_weight_multi, 'REPLACE')
656
657        bpy.ops.object.mode_set(mode=pre_mode)
658        return {'FINISHED'}
659
660
661@compat.BlRegister()
662class CNV_OT_remove_noassign_vertex_groups(bpy.types.Operator):
663    bl_idname = 'object.remove_noassign_vertex_groups'
664    bl_label = "割り当てのない頂点グループを削除"
665    bl_description = "どの頂点にも割り当てられていない頂点グループを全て削除します"
666    bl_options = {'REGISTER', 'UNDO'}
667
668    threshold = bpy.props.FloatProperty(name="これ以下の影響は切り捨て", default=0.000001, min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, step=1, precision=10)
669
670    @classmethod
671    def poll(cls, context):
672        ob = context.active_object
673        if ob and ob.type == 'MESH':
674            return len(ob.vertex_groups) > 0
675        return False
676
677    def invoke(self, context, event):
678        return context.window_manager.invoke_props_dialog(self)
679
680    def draw(self, context):
681        self.layout.prop(self, 'threshold')
682
683    def execute(self, context):
684        ob = context.active_object
685        me = ob.data
686
687        is_keeps = [False for i in range(len(ob.vertex_groups))]
688
689        for vert in me.vertices:
690            for vge in vert.groups:
691                if not is_keeps[vge.group]:
692                    if self.threshold < vge.weight:
693                        is_keeps[vge.group] = True
694
695        copy_vertex_groups = ob.vertex_groups[:]
696        for i in range(len(copy_vertex_groups)):
697            if not is_keeps[i] and not copy_vertex_groups[i].lock_weight:
698                ob.vertex_groups.remove(copy_vertex_groups[i])
699
700        return {'FINISHED'}
@compat.BlRegister()
class CNV_OT_quick_transfer_vertex_group(bpy_types.Operator):
 27@compat.BlRegister()
 28class CNV_OT_quick_transfer_vertex_group(bpy.types.Operator):
 29    bl_idname = 'object.quick_transfer_vertex_group'
 30    bl_label = "クイック・ウェイト転送"
 31    bl_description = "アクティブなメッシュに他の選択メッシュの頂点グループを高速で転送します"
 32    bl_options = {'REGISTER', 'UNDO'}
 33
 34    is_remove_old_vertex_groups = bpy.props.BoolProperty(name="すでにある頂点グループを削除 (ロックで保護)", default=False)
 35    is_source_select_vert_only = bpy.props.BoolProperty(name="選択頂点のみ(参照)", default=False)
 36    is_target_select_vert_only = bpy.props.BoolProperty(name="選択頂点のみ(対象)", default=False)
 37    items = [
 38        ('NEAREST', "最も近い頂点", "", 'VERTEXSEL', 1),
 39        ('EDGEINTERP_NEAREST', "最も近い辺", "", 'EDGESEL', 2),
 40        ('POLYINTERP_NEAREST', "最も近い面", "", 'FACESEL', 3),
 41        ('POLYINTERP_VNORPROJ', "投影先", "", 'MOD_UVPROJECT', 4),
 42    ]
 43    vert_mapping = bpy.props.EnumProperty(items=items, name="参照要素", default='POLYINTERP_NEAREST')
 44    is_clean = bpy.props.BoolProperty(name="転送後にクリーンを実行", default=True)
 45    is_remove_noassign = bpy.props.BoolProperty(name="転送後に割り当てのない頂点グループを削除", default=True)
 46
 47    @classmethod
 48    def poll(cls, context):
 49        obs = context.selected_objects
 50        if len(obs) < 2:
 51            return False
 52
 53        active_ob = context.active_object
 54        for ob in obs:
 55            if ob.type != 'MESH':
 56                return False
 57            if ob.name != active_ob.name:
 58                if not len(ob.vertex_groups):
 59                    return False
 60        return True
 61
 62    def invoke(self, context, event):
 63        return context.window_manager.invoke_props_dialog(self)
 64
 65    def draw(self, context):
 66        self.layout.prop(self, 'is_remove_old_vertex_groups', icon='ERROR')
 67
 68        row = self.layout.row(align=True)
 69        row.prop(self, 'is_source_select_vert_only', icon='UV_SYNC_SELECT')
 70        row.prop(self, 'is_target_select_vert_only', icon='UV_SYNC_SELECT')
 71
 72        self.layout.prop(self, 'vert_mapping')
 73        self.layout.prop(self, 'is_clean', icon='DISCLOSURE_TRI_DOWN')
 74        self.layout.prop(self, 'is_remove_noassign', icon='X')
 75
 76    def execute(self, context):
 77        class MyVertexGroupElement:
 78            pass
 79
 80        target_ob = context.active_object
 81        target_me = target_ob.data
 82
 83        pre_mode = target_ob.mode
 84        bpy.ops.object.mode_set(mode='OBJECT')
 85
 86        original_source_obs = []
 87        for ob in context.selected_objects:
 88            if ob.name != target_ob.name:
 89                original_source_obs.append(ob)
 90
 91        compat.set_select(target_ob, False)
 92        compat.set_active(context, original_source_obs[0])
 93        bpy.ops.object.duplicate(linked=False, mode='TRANSLATION')
 94        if len(context.selected_objects) > 1:
 95            bpy.ops.object.join()
 96        join_source_ob = context.selected_objects[0]
 97        join_source_me = join_source_ob.data
 98
 99        temp_source_ob = join_source_ob.copy()
100        temp_source_me = join_source_me.copy()
101        temp_source_ob.data = temp_source_me
102        compat.link(context.scene, temp_source_ob)
103        compat.set_select(temp_source_ob, True)
104        compat.set_select(join_source_ob, False)
105        compat.set_active(context, temp_source_ob)
106        if self.is_source_select_vert_only:
107            bpy.ops.object.mode_set(mode='EDIT')
108            bpy.ops.mesh.select_all(action='INVERT')
109            bpy.ops.mesh.delete(type='VERT')
110            bpy.ops.object.mode_set(mode='OBJECT')
111
112        compat.set_select(target_ob, True)
113        compat.set_active(context, target_ob)
114
115        if self.vert_mapping == 'POLYINTERP_VNORPROJ' and len(temp_source_me.polygons) == 0:
116            self.vert_mapping = 'EDGEINTERP_NEAREST'
117            self.report(type={'WARNING'}, message="面がひとつも存在しません、辺モードに変更します")
118        if self.vert_mapping == 'POLYINTERP_NEAREST' and len(temp_source_me.polygons) == 0:
119            self.vert_mapping = 'EDGEINTERP_NEAREST'
120            self.report(type={'WARNING'}, message="面がひとつも存在しません、辺モードに変更します")
121        if self.vert_mapping == 'EDGEINTERP_NEAREST' and len(temp_source_me.edges) == 0:
122            self.vert_mapping = 'NEAREST'
123            self.report(type={'WARNING'}, message="辺がひとつも存在しません、頂点モードに変更します")
124        if self.vert_mapping == 'NEAREST' and len(temp_source_me.vertices) == 0:
125            self.report(type={'ERROR'}, message="頂点がひとつも存在しません、中止します")
126            return {'CANCELLED'}
127
128        if self.is_remove_old_vertex_groups:
129            for vg in target_ob.vertex_groups[:]:
130                if not vg.lock_weight:
131                    target_ob.vertex_groups.remove(vg)
132
133        old_vertex_groups = []
134        for vert in target_me.vertices:
135            mvges = []
136            for vge in vert.groups:
137                mvge = MyVertexGroupElement()
138                mvge.vertex_group = target_ob.vertex_groups[vge.group]
139                mvge.weight = vge.weight
140                mvges.append(mvge)
141            old_vertex_groups.append(mvges)
142
143        if self.is_remove_noassign:
144            pre_vertex_group_names = [vg.name for vg in target_ob.vertex_groups]
145
146        bpy.ops.object.data_transfer(use_reverse_transfer=True, use_freeze=False, data_type='VGROUP_WEIGHTS', use_create=True, vert_mapping=self.vert_mapping, use_auto_transform=False, use_object_transform=True, use_max_distance=False, ray_radius=0, layers_select_src='NAME', layers_select_dst='ALL', mix_mode='REPLACE', mix_factor=1)
147        if self.is_clean:
148            bpy.ops.object.vertex_group_clean(group_select_mode='ALL', limit=0.00000000001)
149
150        bpy.ops.object.mode_set(mode='EDIT')
151        bpy.ops.object.mode_set(mode='OBJECT')
152
153        if self.is_remove_noassign:
154            is_keeps = [False for i in range(len(target_ob.vertex_groups))]
155            for vert in target_me.vertices:
156                for vge in vert.groups:
157                    if not is_keeps[vge.group]:
158                        if 0.000001 < vge.weight:
159                            is_keeps[vge.group] = True
160            copy_vertex_groups = target_ob.vertex_groups[:]
161            for i in range(len(copy_vertex_groups)):
162                if not is_keeps[i] and not copy_vertex_groups[i].lock_weight:
163                    if copy_vertex_groups[i].name not in pre_vertex_group_names:
164                        target_ob.vertex_groups.remove(copy_vertex_groups[i])
165
166        if self.is_target_select_vert_only:
167            for vert in target_me.vertices:
168                if not vert.select:
169                    for vg in target_ob.vertex_groups:
170                        vg.remove([vert.index])
171                    for mvge in old_vertex_groups[vert.index]:
172                        mvge.vertex_group.add([vert.index], mvge.weight, 'REPLACE')
173
174        for vert in target_me.vertices:
175            for vg in target_ob.vertex_groups:
176                if vg.lock_weight:
177                    vg.remove([vert.index])
178            for mvge in old_vertex_groups[vert.index]:
179                if mvge.vertex_group.lock_weight:
180                    mvge.vertex_group.add([vert.index], mvge.weight, 'REPLACE')
181
182        common.remove_data([temp_source_ob, temp_source_me])
183        compat.set_select(join_source_ob, True)
184
185        common.remove_data([join_source_ob, join_source_me])
186        for ob in original_source_obs:
187            compat.set_select(ob, True)
188
189        compat.set_active(context, target_ob)
190        bpy.ops.object.mode_set(mode=pre_mode)
191        return {'FINISHED'}
bl_idname = 'object.quick_transfer_vertex_group'
bl_label = 'クイック・ウェイト転送'
bl_description = 'アクティブなメッシュに他の選択メッシュの頂点グループを高速で転送します'
bl_options = {'REGISTER', 'UNDO'}
is_remove_old_vertex_groups: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'すでにある頂点グループを削除 (ロックで保護)', 'default': False, 'attr': 'is_remove_old_vertex_groups'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'すでにある頂点グループを削除 (ロックで保護)', 'default': False, 'attr': 'is_remove_old_vertex_groups'}>
is_source_select_vert_only: <_PropertyDeferred, <built-in function BoolProperty>, {'name': '選択頂点のみ(参照)', 'default': False, 'attr': 'is_source_select_vert_only'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': '選択頂点のみ(参照)', 'default': False, 'attr': 'is_source_select_vert_only'}>
is_target_select_vert_only: <_PropertyDeferred, <built-in function BoolProperty>, {'name': '選択頂点のみ(対象)', 'default': False, 'attr': 'is_target_select_vert_only'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': '選択頂点のみ(対象)', 'default': False, 'attr': 'is_target_select_vert_only'}>
items = [('NEAREST', '最も近い頂点', '', 'VERTEXSEL', 1), ('EDGEINTERP_NEAREST', '最も近い辺', '', 'EDGESEL', 2), ('POLYINTERP_NEAREST', '最も近い面', '', 'FACESEL', 3), ('POLYINTERP_VNORPROJ', '投影先', '', 'MOD_UVPROJECT', 4)]
vert_mapping: <_PropertyDeferred, <built-in function EnumProperty>, {'items': [('NEAREST', '最も近い頂点', '', 'VERTEXSEL', 1), ('EDGEINTERP_NEAREST', '最も近い辺', '', 'EDGESEL', 2), ('POLYINTERP_NEAREST', '最も近い面', '', 'FACESEL', 3), ('POLYINTERP_VNORPROJ', '投影先', '', 'MOD_UVPROJECT', 4)], 'name': '参照要素', 'default': 'POLYINTERP_NEAREST', 'attr': 'vert_mapping'}> = <_PropertyDeferred, <built-in function EnumProperty>, {'items': [('NEAREST', '最も近い頂点', '', 'VERTEXSEL', 1), ('EDGEINTERP_NEAREST', '最も近い辺', '', 'EDGESEL', 2), ('POLYINTERP_NEAREST', '最も近い面', '', 'FACESEL', 3), ('POLYINTERP_VNORPROJ', '投影先', '', 'MOD_UVPROJECT', 4)], 'name': '参照要素', 'default': 'POLYINTERP_NEAREST', 'attr': 'vert_mapping'}>
is_clean: <_PropertyDeferred, <built-in function BoolProperty>, {'name': '転送後にクリーンを実行', 'default': True, 'attr': 'is_clean'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': '転送後にクリーンを実行', 'default': True, 'attr': 'is_clean'}>
is_remove_noassign: <_PropertyDeferred, <built-in function BoolProperty>, {'name': '転送後に割り当てのない頂点グループを削除', 'default': True, 'attr': 'is_remove_noassign'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': '転送後に割り当てのない頂点グループを削除', 'default': True, 'attr': 'is_remove_noassign'}>
@classmethod
def poll(cls, context):
47    @classmethod
48    def poll(cls, context):
49        obs = context.selected_objects
50        if len(obs) < 2:
51            return False
52
53        active_ob = context.active_object
54        for ob in obs:
55            if ob.type != 'MESH':
56                return False
57            if ob.name != active_ob.name:
58                if not len(ob.vertex_groups):
59                    return False
60        return True
def invoke(self, context, event):
62    def invoke(self, context, event):
63        return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
65    def draw(self, context):
66        self.layout.prop(self, 'is_remove_old_vertex_groups', icon='ERROR')
67
68        row = self.layout.row(align=True)
69        row.prop(self, 'is_source_select_vert_only', icon='UV_SYNC_SELECT')
70        row.prop(self, 'is_target_select_vert_only', icon='UV_SYNC_SELECT')
71
72        self.layout.prop(self, 'vert_mapping')
73        self.layout.prop(self, 'is_clean', icon='DISCLOSURE_TRI_DOWN')
74        self.layout.prop(self, 'is_remove_noassign', icon='X')
def execute(self, context):
 76    def execute(self, context):
 77        class MyVertexGroupElement:
 78            pass
 79
 80        target_ob = context.active_object
 81        target_me = target_ob.data
 82
 83        pre_mode = target_ob.mode
 84        bpy.ops.object.mode_set(mode='OBJECT')
 85
 86        original_source_obs = []
 87        for ob in context.selected_objects:
 88            if ob.name != target_ob.name:
 89                original_source_obs.append(ob)
 90
 91        compat.set_select(target_ob, False)
 92        compat.set_active(context, original_source_obs[0])
 93        bpy.ops.object.duplicate(linked=False, mode='TRANSLATION')
 94        if len(context.selected_objects) > 1:
 95            bpy.ops.object.join()
 96        join_source_ob = context.selected_objects[0]
 97        join_source_me = join_source_ob.data
 98
 99        temp_source_ob = join_source_ob.copy()
100        temp_source_me = join_source_me.copy()
101        temp_source_ob.data = temp_source_me
102        compat.link(context.scene, temp_source_ob)
103        compat.set_select(temp_source_ob, True)
104        compat.set_select(join_source_ob, False)
105        compat.set_active(context, temp_source_ob)
106        if self.is_source_select_vert_only:
107            bpy.ops.object.mode_set(mode='EDIT')
108            bpy.ops.mesh.select_all(action='INVERT')
109            bpy.ops.mesh.delete(type='VERT')
110            bpy.ops.object.mode_set(mode='OBJECT')
111
112        compat.set_select(target_ob, True)
113        compat.set_active(context, target_ob)
114
115        if self.vert_mapping == 'POLYINTERP_VNORPROJ' and len(temp_source_me.polygons) == 0:
116            self.vert_mapping = 'EDGEINTERP_NEAREST'
117            self.report(type={'WARNING'}, message="面がひとつも存在しません、辺モードに変更します")
118        if self.vert_mapping == 'POLYINTERP_NEAREST' and len(temp_source_me.polygons) == 0:
119            self.vert_mapping = 'EDGEINTERP_NEAREST'
120            self.report(type={'WARNING'}, message="面がひとつも存在しません、辺モードに変更します")
121        if self.vert_mapping == 'EDGEINTERP_NEAREST' and len(temp_source_me.edges) == 0:
122            self.vert_mapping = 'NEAREST'
123            self.report(type={'WARNING'}, message="辺がひとつも存在しません、頂点モードに変更します")
124        if self.vert_mapping == 'NEAREST' and len(temp_source_me.vertices) == 0:
125            self.report(type={'ERROR'}, message="頂点がひとつも存在しません、中止します")
126            return {'CANCELLED'}
127
128        if self.is_remove_old_vertex_groups:
129            for vg in target_ob.vertex_groups[:]:
130                if not vg.lock_weight:
131                    target_ob.vertex_groups.remove(vg)
132
133        old_vertex_groups = []
134        for vert in target_me.vertices:
135            mvges = []
136            for vge in vert.groups:
137                mvge = MyVertexGroupElement()
138                mvge.vertex_group = target_ob.vertex_groups[vge.group]
139                mvge.weight = vge.weight
140                mvges.append(mvge)
141            old_vertex_groups.append(mvges)
142
143        if self.is_remove_noassign:
144            pre_vertex_group_names = [vg.name for vg in target_ob.vertex_groups]
145
146        bpy.ops.object.data_transfer(use_reverse_transfer=True, use_freeze=False, data_type='VGROUP_WEIGHTS', use_create=True, vert_mapping=self.vert_mapping, use_auto_transform=False, use_object_transform=True, use_max_distance=False, ray_radius=0, layers_select_src='NAME', layers_select_dst='ALL', mix_mode='REPLACE', mix_factor=1)
147        if self.is_clean:
148            bpy.ops.object.vertex_group_clean(group_select_mode='ALL', limit=0.00000000001)
149
150        bpy.ops.object.mode_set(mode='EDIT')
151        bpy.ops.object.mode_set(mode='OBJECT')
152
153        if self.is_remove_noassign:
154            is_keeps = [False for i in range(len(target_ob.vertex_groups))]
155            for vert in target_me.vertices:
156                for vge in vert.groups:
157                    if not is_keeps[vge.group]:
158                        if 0.000001 < vge.weight:
159                            is_keeps[vge.group] = True
160            copy_vertex_groups = target_ob.vertex_groups[:]
161            for i in range(len(copy_vertex_groups)):
162                if not is_keeps[i] and not copy_vertex_groups[i].lock_weight:
163                    if copy_vertex_groups[i].name not in pre_vertex_group_names:
164                        target_ob.vertex_groups.remove(copy_vertex_groups[i])
165
166        if self.is_target_select_vert_only:
167            for vert in target_me.vertices:
168                if not vert.select:
169                    for vg in target_ob.vertex_groups:
170                        vg.remove([vert.index])
171                    for mvge in old_vertex_groups[vert.index]:
172                        mvge.vertex_group.add([vert.index], mvge.weight, 'REPLACE')
173
174        for vert in target_me.vertices:
175            for vg in target_ob.vertex_groups:
176                if vg.lock_weight:
177                    vg.remove([vert.index])
178            for mvge in old_vertex_groups[vert.index]:
179                if mvge.vertex_group.lock_weight:
180                    mvge.vertex_group.add([vert.index], mvge.weight, 'REPLACE')
181
182        common.remove_data([temp_source_ob, temp_source_me])
183        compat.set_select(join_source_ob, True)
184
185        common.remove_data([join_source_ob, join_source_me])
186        for ob in original_source_obs:
187            compat.set_select(ob, True)
188
189        compat.set_active(context, target_ob)
190        bpy.ops.object.mode_set(mode=pre_mode)
191        return {'FINISHED'}
bl_rna = <bpy_struct, Struct("OBJECT_OT_quick_transfer_vertex_group")>
Inherited Members
bpy_types.Operator
as_keywords
poll_message_set
builtins.bpy_struct
keys
values
get
pop
as_pointer
keyframe_insert
keyframe_delete
driver_add
driver_remove
is_property_set
property_unset
is_property_hidden
is_property_readonly
is_property_overridable_library
property_overridable_library_set
path_resolve
path_from_id
type_recast
bl_rna_get_subclass_py
bl_rna_get_subclass
id_properties_ensure
id_properties_clear
id_properties_ui
id_data
@compat.BlRegister()
class CNV_OT_precision_transfer_vertex_group(bpy_types.Operator):
194@compat.BlRegister()
195class CNV_OT_precision_transfer_vertex_group(bpy.types.Operator):
196    bl_idname = 'object.precision_transfer_vertex_group'
197    bl_label = "空間ぼかし・ウェイト転送"
198    bl_description = "アクティブなメッシュに他の選択メッシュの頂点グループを遠いほどぼかして転送します"
199    bl_options = {'REGISTER', 'UNDO'}
200
201    is_first_remove_all = bpy.props.BoolProperty(name="すでにある頂点グループを削除 (ロックで保護)", default=False)
202    subdivide_number = bpy.props.IntProperty(name="参照元の分割", default=1, min=0, max=10, soft_min=0, soft_max=10)
203    extend_range = bpy.props.FloatProperty(name="範囲倍率", default=1.1, min=1.0001, max=5.0, soft_min=1.0001, soft_max=5.0, step=10, precision=2)
204    is_remove_empty = bpy.props.BoolProperty(name="割り当てのない頂点グループを削除", default=True)
205
206    @classmethod
207    def poll(cls, context):
208        active_ob = context.active_object
209        obs = context.selected_objects
210        if len(obs) != 2:
211            return False
212
213        for ob in obs:
214            if ob.type != 'MESH':
215                return False
216            if ob.name != active_ob.name:
217                if len(ob.vertex_groups):
218                    return True
219        return False
220
221    def invoke(self, context, event):
222        return context.window_manager.invoke_props_dialog(self)
223
224    def draw(self, context):
225        self.layout.prop(self, 'is_first_remove_all', icon='ERROR')
226        self.layout.prop(self, 'subdivide_number', icon='LATTICE_DATA')
227        self.layout.prop(self, 'extend_range', icon='PROP_ON')
228        self.layout.prop(self, 'is_remove_empty', icon='X')
229
230    def execute(self, context):
231        start_time = time.time()
232
233        target_ob = context.active_object
234        target_me = target_ob.data
235
236        pre_mode = target_ob.mode
237        bpy.ops.object.mode_set(mode='OBJECT')
238
239        for ob in context.selected_objects:
240            if ob.name != target_ob.name:
241                source_original_ob = ob
242                break
243        source_ob = source_original_ob.copy()
244        source_me = source_original_ob.data.copy()
245        source_ob.data = source_me
246        compat.link(context.scene, source_ob)
247
248        compat.set_select(target_ob, False)
249        compat.set_select(source_original_ob, False)
250        compat.set_active(context, source_ob)
251        try:
252            bpy.ops.object.mode_set(mode='EDIT')
253            bpy.ops.mesh.reveal()
254            bpy.ops.mesh.select_all(action='SELECT')
255            bpy.ops.mesh.subdivide(number_cuts=self.subdivide_number, smoothness=0.0, quadcorner='STRAIGHT_CUT', fractal=0.0, fractal_along_normal=0.0, seed=0)
256            bpy.ops.object.mode_set(mode='OBJECT')
257
258            if self.is_first_remove_all:
259                for vg in target_ob.vertex_groups[:]:
260                    if not vg.lock_weight:
261                        target_ob.vertex_groups.remove(vg)
262
263            kd = mathutils.kdtree.KDTree(len(source_me.vertices))
264            for vert in source_me.vertices:
265                co = compat.mul(source_ob.matrix_world, vert.co)
266                kd.insert(co, vert.index)
267            kd.balance()
268
269            context.window_manager.progress_begin(0, len(target_me.vertices))
270            progress_reduce = len(target_me.vertices) // 200 + 1
271            near_vert_data = []
272            near_vert_multi_total = []
273            near_vert_multi_total_append = near_vert_multi_total.append
274            for vert in target_me.vertices:
275                near_vert_data.append([])
276                near_vert_data_append = near_vert_data[-1].append
277
278                target_co = compat.mul(target_ob.matrix_world, vert.co)
279
280                mini_co, mini_index, mini_dist = kd.find(target_co)
281                radius = mini_dist * self.extend_range
282                diff_radius = radius - mini_dist
283
284                multi_total = 0.0
285                for co, index, dist in kd.find_range(target_co, radius):
286                    if 0 < diff_radius:
287                        multi = (diff_radius - (dist - mini_dist)) / diff_radius
288                    else:
289                        multi = 1.0
290                    near_vert_data_append((index, multi))
291                    multi_total += multi
292                near_vert_multi_total_append(multi_total)
293
294                if vert.index % progress_reduce == 0:
295                    context.window_manager.progress_update(vert.index)
296            context.window_manager.progress_end()
297
298            context.window_manager.progress_begin(0, len(source_ob.vertex_groups))
299            for source_vertex_group in source_ob.vertex_groups:
300
301                if source_vertex_group.name in target_ob.vertex_groups:
302                    target_vertex_group = target_ob.vertex_groups[source_vertex_group.name]
303                else:
304                    target_vertex_group = target_ob.vertex_groups.new(name=source_vertex_group.name)
305
306                is_waighted = False
307
308                source_weights = []
309                source_weights_append = source_weights.append
310                for source_vert in source_me.vertices:
311                    for elem in source_vert.groups:
312                        if elem.group == source_vertex_group.index:
313                            source_weights_append(elem.weight)
314                            break
315                    else:
316                        source_weights_append(0.0)
317
318                for target_vert in target_me.vertices:
319
320                    if 0 < near_vert_multi_total[target_vert.index]:
321
322                        total_weight = [source_weights[i] * m for i, m in near_vert_data[target_vert.index]]
323                        total_weight = sum(total_weight)
324
325                        average_weight = total_weight / near_vert_multi_total[target_vert.index]
326                    else:
327                        average_weight = 0.0
328
329                    if 0.000001 < average_weight:
330                        target_vertex_group.add([target_vert.index], average_weight, 'REPLACE')
331                        is_waighted = True
332                    else:
333                        if not self.is_first_remove_all:
334                            target_vertex_group.remove([target_vert.index])
335
336                context.window_manager.progress_update(source_vertex_group.index)
337
338                if not is_waighted and self.is_remove_empty:
339                    target_ob.vertex_groups.remove(target_vertex_group)
340            context.window_manager.progress_end()
341
342            target_ob.vertex_groups.active_index = 0
343        finally:
344            common.remove_data([source_ob, source_me])
345            compat.set_select(source_original_ob, True)
346            compat.set_select(target_ob, True)
347            compat.set_active(context, target_ob)
348            bpy.ops.object.mode_set(mode=pre_mode)
349
350        diff_time = time.time() - start_time
351        self.report(type={'INFO'}, message=f_tip_("{:.2f} Seconds", diff_time))
352        return {'FINISHED'}
bl_idname = 'object.precision_transfer_vertex_group'
bl_label = '空間ぼかし・ウェイト転送'
bl_description = 'アクティブなメッシュに他の選択メッシュの頂点グループを遠いほどぼかして転送します'
bl_options = {'REGISTER', 'UNDO'}
is_first_remove_all: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'すでにある頂点グループを削除 (ロックで保護)', 'default': False, 'attr': 'is_first_remove_all'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'すでにある頂点グループを削除 (ロックで保護)', 'default': False, 'attr': 'is_first_remove_all'}>
subdivide_number: <_PropertyDeferred, <built-in function IntProperty>, {'name': '参照元の分割', 'default': 1, 'min': 0, 'max': 10, 'soft_min': 0, 'soft_max': 10, 'attr': 'subdivide_number'}> = <_PropertyDeferred, <built-in function IntProperty>, {'name': '参照元の分割', 'default': 1, 'min': 0, 'max': 10, 'soft_min': 0, 'soft_max': 10, 'attr': 'subdivide_number'}>
extend_range: <_PropertyDeferred, <built-in function FloatProperty>, {'name': '範囲倍率', 'default': 1.1, 'min': 1.0001, 'max': 5.0, 'soft_min': 1.0001, 'soft_max': 5.0, 'step': 10, 'precision': 2, 'attr': 'extend_range'}> = <_PropertyDeferred, <built-in function FloatProperty>, {'name': '範囲倍率', 'default': 1.1, 'min': 1.0001, 'max': 5.0, 'soft_min': 1.0001, 'soft_max': 5.0, 'step': 10, 'precision': 2, 'attr': 'extend_range'}>
is_remove_empty: <_PropertyDeferred, <built-in function BoolProperty>, {'name': '割り当てのない頂点グループを削除', 'default': True, 'attr': 'is_remove_empty'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': '割り当てのない頂点グループを削除', 'default': True, 'attr': 'is_remove_empty'}>
@classmethod
def poll(cls, context):
206    @classmethod
207    def poll(cls, context):
208        active_ob = context.active_object
209        obs = context.selected_objects
210        if len(obs) != 2:
211            return False
212
213        for ob in obs:
214            if ob.type != 'MESH':
215                return False
216            if ob.name != active_ob.name:
217                if len(ob.vertex_groups):
218                    return True
219        return False
def invoke(self, context, event):
221    def invoke(self, context, event):
222        return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
224    def draw(self, context):
225        self.layout.prop(self, 'is_first_remove_all', icon='ERROR')
226        self.layout.prop(self, 'subdivide_number', icon='LATTICE_DATA')
227        self.layout.prop(self, 'extend_range', icon='PROP_ON')
228        self.layout.prop(self, 'is_remove_empty', icon='X')
def execute(self, context):
230    def execute(self, context):
231        start_time = time.time()
232
233        target_ob = context.active_object
234        target_me = target_ob.data
235
236        pre_mode = target_ob.mode
237        bpy.ops.object.mode_set(mode='OBJECT')
238
239        for ob in context.selected_objects:
240            if ob.name != target_ob.name:
241                source_original_ob = ob
242                break
243        source_ob = source_original_ob.copy()
244        source_me = source_original_ob.data.copy()
245        source_ob.data = source_me
246        compat.link(context.scene, source_ob)
247
248        compat.set_select(target_ob, False)
249        compat.set_select(source_original_ob, False)
250        compat.set_active(context, source_ob)
251        try:
252            bpy.ops.object.mode_set(mode='EDIT')
253            bpy.ops.mesh.reveal()
254            bpy.ops.mesh.select_all(action='SELECT')
255            bpy.ops.mesh.subdivide(number_cuts=self.subdivide_number, smoothness=0.0, quadcorner='STRAIGHT_CUT', fractal=0.0, fractal_along_normal=0.0, seed=0)
256            bpy.ops.object.mode_set(mode='OBJECT')
257
258            if self.is_first_remove_all:
259                for vg in target_ob.vertex_groups[:]:
260                    if not vg.lock_weight:
261                        target_ob.vertex_groups.remove(vg)
262
263            kd = mathutils.kdtree.KDTree(len(source_me.vertices))
264            for vert in source_me.vertices:
265                co = compat.mul(source_ob.matrix_world, vert.co)
266                kd.insert(co, vert.index)
267            kd.balance()
268
269            context.window_manager.progress_begin(0, len(target_me.vertices))
270            progress_reduce = len(target_me.vertices) // 200 + 1
271            near_vert_data = []
272            near_vert_multi_total = []
273            near_vert_multi_total_append = near_vert_multi_total.append
274            for vert in target_me.vertices:
275                near_vert_data.append([])
276                near_vert_data_append = near_vert_data[-1].append
277
278                target_co = compat.mul(target_ob.matrix_world, vert.co)
279
280                mini_co, mini_index, mini_dist = kd.find(target_co)
281                radius = mini_dist * self.extend_range
282                diff_radius = radius - mini_dist
283
284                multi_total = 0.0
285                for co, index, dist in kd.find_range(target_co, radius):
286                    if 0 < diff_radius:
287                        multi = (diff_radius - (dist - mini_dist)) / diff_radius
288                    else:
289                        multi = 1.0
290                    near_vert_data_append((index, multi))
291                    multi_total += multi
292                near_vert_multi_total_append(multi_total)
293
294                if vert.index % progress_reduce == 0:
295                    context.window_manager.progress_update(vert.index)
296            context.window_manager.progress_end()
297
298            context.window_manager.progress_begin(0, len(source_ob.vertex_groups))
299            for source_vertex_group in source_ob.vertex_groups:
300
301                if source_vertex_group.name in target_ob.vertex_groups:
302                    target_vertex_group = target_ob.vertex_groups[source_vertex_group.name]
303                else:
304                    target_vertex_group = target_ob.vertex_groups.new(name=source_vertex_group.name)
305
306                is_waighted = False
307
308                source_weights = []
309                source_weights_append = source_weights.append
310                for source_vert in source_me.vertices:
311                    for elem in source_vert.groups:
312                        if elem.group == source_vertex_group.index:
313                            source_weights_append(elem.weight)
314                            break
315                    else:
316                        source_weights_append(0.0)
317
318                for target_vert in target_me.vertices:
319
320                    if 0 < near_vert_multi_total[target_vert.index]:
321
322                        total_weight = [source_weights[i] * m for i, m in near_vert_data[target_vert.index]]
323                        total_weight = sum(total_weight)
324
325                        average_weight = total_weight / near_vert_multi_total[target_vert.index]
326                    else:
327                        average_weight = 0.0
328
329                    if 0.000001 < average_weight:
330                        target_vertex_group.add([target_vert.index], average_weight, 'REPLACE')
331                        is_waighted = True
332                    else:
333                        if not self.is_first_remove_all:
334                            target_vertex_group.remove([target_vert.index])
335
336                context.window_manager.progress_update(source_vertex_group.index)
337
338                if not is_waighted and self.is_remove_empty:
339                    target_ob.vertex_groups.remove(target_vertex_group)
340            context.window_manager.progress_end()
341
342            target_ob.vertex_groups.active_index = 0
343        finally:
344            common.remove_data([source_ob, source_me])
345            compat.set_select(source_original_ob, True)
346            compat.set_select(target_ob, True)
347            compat.set_active(context, target_ob)
348            bpy.ops.object.mode_set(mode=pre_mode)
349
350        diff_time = time.time() - start_time
351        self.report(type={'INFO'}, message=f_tip_("{:.2f} Seconds", diff_time))
352        return {'FINISHED'}
bl_rna = <bpy_struct, Struct("OBJECT_OT_precision_transfer_vertex_group")>
Inherited Members
bpy_types.Operator
as_keywords
poll_message_set
builtins.bpy_struct
keys
values
items
get
pop
as_pointer
keyframe_insert
keyframe_delete
driver_add
driver_remove
is_property_set
property_unset
is_property_hidden
is_property_readonly
is_property_overridable_library
property_overridable_library_set
path_resolve
path_from_id
type_recast
bl_rna_get_subclass_py
bl_rna_get_subclass
id_properties_ensure
id_properties_clear
id_properties_ui
id_data
@compat.BlRegister()
class CNV_OT_quick_blur_vertex_group(bpy_types.Operator):
355@compat.BlRegister()
356class CNV_OT_quick_blur_vertex_group(bpy.types.Operator):
357    bl_idname = 'object.quick_blur_vertex_group'
358    bl_label = "頂点グループぼかし"
359    bl_description = "アクティブ、もしくは全ての頂点グループをぼかします"
360    bl_options = {'REGISTER', 'UNDO'}
361
362    items = [
363        ('ACTIVE', "アクティブのみ", "", 'HAND', 1),
364        ('ALL', "全て", "", 'ARROW_LEFTRIGHT', 2),
365    ]
366    target = bpy.props.EnumProperty(items=items, name="対象", default='ALL')
367    strength = bpy.props.FloatProperty(name="強さ", default=1.0, min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, step=10, precision=3)
368    count = bpy.props.IntProperty(name="反復", default=1, min=1, max=256, soft_min=1, soft_max=256)
369    size = bpy.props.FloatProperty(name="拡大縮小", default=0.0, min=-1.0, max=1.0, soft_min=-1.0, soft_max=1.0, step=10, precision=3)
370
371    @classmethod
372    def poll(cls, context):
373        ob = context.active_object
374        if ob and ob.type == 'MESH':
375            return ob.vertex_groups.active
376        return False
377
378    def invoke(self, context, event):
379        return context.window_manager.invoke_props_dialog(self)
380
381    def draw(self, context):
382        self.layout.prop(self, 'target', icon='VIEWZOOM')
383        self.layout.prop(self, 'strength')
384        self.layout.prop(self, 'count')
385        self.layout.prop(self, 'size')
386
387    def execute(self, context):
388        target_ob = context.active_object
389        target_me = target_ob.data
390
391        pre_mode = target_ob.mode
392        bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
393
394        pre_use_paint_mask_vertex = target_me.use_paint_mask_vertex
395        target_me.use_paint_mask_vertex = True
396
397        bpy.ops.paint.vert_select_all(action='SELECT')
398        # source='ALL'は2.8で削除された
399        bpy.ops.object.vertex_group_smooth(group_select_mode=self.target, factor=self.strength, repeat=self.count, expand=self.size)
400
401        target_me.use_paint_mask_vertex = pre_use_paint_mask_vertex
402
403        bpy.ops.object.mode_set(mode=pre_mode)
404        return {'FINISHED'}
bl_idname = 'object.quick_blur_vertex_group'
bl_label = '頂点グループぼかし'
bl_description = 'アクティブ、もしくは全ての頂点グループをぼかします'
bl_options = {'REGISTER', 'UNDO'}
items = [('ACTIVE', 'アクティブのみ', '', 'HAND', 1), ('ALL', '全て', '', 'ARROW_LEFTRIGHT', 2)]
target: <_PropertyDeferred, <built-in function EnumProperty>, {'items': [('ACTIVE', 'アクティブのみ', '', 'HAND', 1), ('ALL', '全て', '', 'ARROW_LEFTRIGHT', 2)], 'name': '対象', 'default': 'ALL', 'attr': 'target'}> = <_PropertyDeferred, <built-in function EnumProperty>, {'items': [('ACTIVE', 'アクティブのみ', '', 'HAND', 1), ('ALL', '全て', '', 'ARROW_LEFTRIGHT', 2)], 'name': '対象', 'default': 'ALL', 'attr': 'target'}>
strength: <_PropertyDeferred, <built-in function FloatProperty>, {'name': '強さ', 'default': 1.0, 'min': 0.0, 'max': 1.0, 'soft_min': 0.0, 'soft_max': 1.0, 'step': 10, 'precision': 3, 'attr': 'strength'}> = <_PropertyDeferred, <built-in function FloatProperty>, {'name': '強さ', 'default': 1.0, 'min': 0.0, 'max': 1.0, 'soft_min': 0.0, 'soft_max': 1.0, 'step': 10, 'precision': 3, 'attr': 'strength'}>
count: <_PropertyDeferred, <built-in function IntProperty>, {'name': '反復', 'default': 1, 'min': 1, 'max': 256, 'soft_min': 1, 'soft_max': 256, 'attr': 'count'}> = <_PropertyDeferred, <built-in function IntProperty>, {'name': '反復', 'default': 1, 'min': 1, 'max': 256, 'soft_min': 1, 'soft_max': 256, 'attr': 'count'}>
size: <_PropertyDeferred, <built-in function FloatProperty>, {'name': '拡大縮小', 'default': 0.0, 'min': -1.0, 'max': 1.0, 'soft_min': -1.0, 'soft_max': 1.0, 'step': 10, 'precision': 3, 'attr': 'size'}> = <_PropertyDeferred, <built-in function FloatProperty>, {'name': '拡大縮小', 'default': 0.0, 'min': -1.0, 'max': 1.0, 'soft_min': -1.0, 'soft_max': 1.0, 'step': 10, 'precision': 3, 'attr': 'size'}>
@classmethod
def poll(cls, context):
371    @classmethod
372    def poll(cls, context):
373        ob = context.active_object
374        if ob and ob.type == 'MESH':
375            return ob.vertex_groups.active
376        return False
def invoke(self, context, event):
378    def invoke(self, context, event):
379        return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
381    def draw(self, context):
382        self.layout.prop(self, 'target', icon='VIEWZOOM')
383        self.layout.prop(self, 'strength')
384        self.layout.prop(self, 'count')
385        self.layout.prop(self, 'size')
def execute(self, context):
387    def execute(self, context):
388        target_ob = context.active_object
389        target_me = target_ob.data
390
391        pre_mode = target_ob.mode
392        bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
393
394        pre_use_paint_mask_vertex = target_me.use_paint_mask_vertex
395        target_me.use_paint_mask_vertex = True
396
397        bpy.ops.paint.vert_select_all(action='SELECT')
398        # source='ALL'は2.8で削除された
399        bpy.ops.object.vertex_group_smooth(group_select_mode=self.target, factor=self.strength, repeat=self.count, expand=self.size)
400
401        target_me.use_paint_mask_vertex = pre_use_paint_mask_vertex
402
403        bpy.ops.object.mode_set(mode=pre_mode)
404        return {'FINISHED'}
bl_rna = <bpy_struct, Struct("OBJECT_OT_quick_blur_vertex_group")>
Inherited Members
bpy_types.Operator
as_keywords
poll_message_set
builtins.bpy_struct
keys
values
get
pop
as_pointer
keyframe_insert
keyframe_delete
driver_add
driver_remove
is_property_set
property_unset
is_property_hidden
is_property_readonly
is_property_overridable_library
property_overridable_library_set
path_resolve
path_from_id
type_recast
bl_rna_get_subclass_py
bl_rna_get_subclass
id_properties_ensure
id_properties_clear
id_properties_ui
id_data
@compat.BlRegister()
class CNV_OT_blur_vertex_group(bpy_types.Operator):
407@compat.BlRegister()
408class CNV_OT_blur_vertex_group(bpy.types.Operator):
409    bl_idname = 'object.blur_vertex_group'
410    bl_label = "旧・頂点グループぼかし"
411    bl_description = "アクティブ、もしくは全ての頂点グループをぼかします"
412    bl_options = {'REGISTER', 'UNDO'}
413
414    items = [
415        ('ACTIVE', "アクティブのみ", "", 'HAND', 1),
416        ('UP', "アクティブより上", "", 'TRIA_UP_BAR', 2),
417        ('DOWN', "アクティブより下", "", 'TRIA_DOWN_BAR', 3),
418        ('ALL', "全て", "", 'ARROW_LEFTRIGHT', 4),
419    ]
420    target = bpy.props.EnumProperty(items=items, name="対象", default='ACTIVE')
421    radius = bpy.props.FloatProperty(name="範囲倍率", default=3, min=0.1, max=50, soft_min=0.1, soft_max=50, step=50, precision=2)
422    strength = bpy.props.IntProperty(name="強さ", default=1, min=1, max=10, soft_min=1, soft_max=10)
423    items = [
424        ('BOTH', "増減両方", "", 'AUTOMERGE_ON', 1),
425        ('ADD', "増加のみ", "", 'TRIA_UP', 2),
426        ('SUB', "減少のみ", "", 'TRIA_DOWN', 3),
427    ]
428    effect = bpy.props.EnumProperty(items=items, name="ぼかし効果", default='BOTH')
429    is_normalize = bpy.props.BoolProperty(name="他頂点グループも調節", default=True)
430
431    @classmethod
432    def poll(cls, context):
433        ob = context.active_object
434        if ob and ob.type == 'MESH':
435            return ob.vertex_groups.active
436        return False
437
438    def invoke(self, context, event):
439        return context.window_manager.invoke_props_dialog(self)
440
441    def draw(self, context):
442        self.layout.prop(self, 'target', icon='VIEWZOOM')
443        self.layout.prop(self, 'radius', icon='PROP_ON')
444        self.layout.prop(self, 'strength', icon='ARROW_LEFTRIGHT')
445        self.layout.prop(self, 'effect', icon='BRUSH_BLUR')
446        self.layout.prop(self, 'is_normalize', icon='GROUP')
447
448    def execute(self, context):
449        ob = context.active_object
450        me = ob.data
451
452        pre_mode = ob.mode
453        bpy.ops.object.mode_set(mode='OBJECT')
454
455        bm = bmesh.new()
456        bm.from_mesh(me)
457        edge_lengths = [e.calc_length() for e in bm.edges]
458        bm.free()
459        edge_lengths.sort()
460        average_edge_length = sum(edge_lengths) / len(edge_lengths)
461        center_index = int((len(edge_lengths) - 1) / 2.0)
462        average_edge_length = (average_edge_length + edge_lengths[center_index]) / 2
463        radius = average_edge_length * self.radius
464
465        context.window_manager.progress_begin(0, len(me.vertices))
466        progress_reduce = len(me.vertices) // 200 + 1
467        near_vert_data = []
468        kd = mathutils.kdtree.KDTree(len(me.vertices))
469        for vert in me.vertices:
470            kd.insert(vert.co.copy(), vert.index)
471        kd.balance()
472        for vert in me.vertices:
473            new_vert_datum = []
474            near_vert_data.append(new_vert_datum)
475            near_vert_data_append = new_vert_datum.append
476            for co, index, dist in kd.find_range(vert.co, radius):
477                multi = (radius - dist) / radius
478                near_vert_data_append((index, multi))
479            if vert.index % progress_reduce == 0:
480                context.window_manager.progress_update(vert.index)
481        context.window_manager.progress_end()
482
483        target_vertex_groups = []
484        if self.target == 'ACTIVE':
485            target_vertex_groups.append(ob.vertex_groups.active)
486        elif self.target == 'UP':
487            for vertex_group in ob.vertex_groups:
488                if vertex_group.index <= ob.vertex_groups.active_index:
489                    target_vertex_groups.append(vertex_group)
490        elif self.target == 'DOWN':
491            for vertex_group in ob.vertex_groups:
492                if ob.vertex_groups.active_index <= vertex_group.index:
493                    target_vertex_groups.append(vertex_group)
494        elif self.target == 'ALL':
495            for vertex_group in ob.vertex_groups:
496                target_vertex_groups.append(vertex_group)
497
498        progress_total = len(target_vertex_groups) * self.strength * len(me.vertices)
499        context.window_manager.progress_begin(0, progress_total)
500        progress_reduce = progress_total // 200 + 1
501        progress_count = 0
502        for strength_count in range(self.strength):
503            for vertex_group in target_vertex_groups:
504
505                weights = []
506                weights_append = weights.append
507                for vert in me.vertices:
508                    for elem in vert.groups:
509                        if elem.group == vertex_group.index:
510                            weights_append(elem.weight)
511                            break
512                    else:
513                        weights_append(0.0)
514
515                for vert in me.vertices:
516
517                    target_weight = weights[vert.index]
518
519                    total_weight = 0.0
520                    total_multi = 0.0
521                    for index, multi in near_vert_data[vert.index]:
522                        if self.effect == 'ADD':
523                            if target_weight <= weights[index]:
524                                total_weight += weights[index] * multi
525                                total_multi += multi
526                        elif self.effect == 'SUB':
527                            if weights[index] <= target_weight:
528                                total_weight += weights[index] * multi
529                                total_multi += multi
530                        else:
531                            total_weight += weights[index] * multi
532                            total_multi += multi
533
534                    if 0 < total_multi:
535                        average_weight = total_weight / total_multi
536                    else:
537                        average_weight = 0.0
538
539                    if 0.001 < average_weight:
540                        vertex_group.add([vert.index], average_weight, 'REPLACE')
541                    else:
542                        vertex_group.remove([vert.index])
543
544                    progress_count += 1
545                    if progress_count % progress_reduce == 0:
546                        context.window_manager.progress_update(progress_count)
547
548                    if self.is_normalize:
549
550                        other_weight_total = 0.0
551                        for elem in vert.groups:
552                            if elem.group != vertex_group.index:
553                                other_weight_total += elem.weight
554
555                        diff_weight = average_weight - target_weight
556                        new_other_weight_total = other_weight_total - diff_weight
557                        if 0 < other_weight_total:
558                            other_weight_multi = new_other_weight_total / other_weight_total
559                        else:
560                            other_weight_multi = 0.0
561
562                        for elem in vert.groups:
563                            if elem.group != vertex_group.index:
564                                vg = ob.vertex_groups[elem.group]
565                                vg.add([vert.index], elem.weight * other_weight_multi, 'REPLACE')
566
567        context.window_manager.progress_end()
568        bpy.ops.object.mode_set(mode=pre_mode)
569        return {'FINISHED'}
bl_idname = 'object.blur_vertex_group'
bl_label = '旧・頂点グループぼかし'
bl_description = 'アクティブ、もしくは全ての頂点グループをぼかします'
bl_options = {'REGISTER', 'UNDO'}
items = [('BOTH', '増減両方', '', 'AUTOMERGE_ON', 1), ('ADD', '増加のみ', '', 'TRIA_UP', 2), ('SUB', '減少のみ', '', 'TRIA_DOWN', 3)]
target: <_PropertyDeferred, <built-in function EnumProperty>, {'items': [('ACTIVE', 'アクティブのみ', '', 'HAND', 1), ('UP', 'アクティブより上', '', 'TRIA_UP_BAR', 2), ('DOWN', 'アクティブより下', '', 'TRIA_DOWN_BAR', 3), ('ALL', '全て', '', 'ARROW_LEFTRIGHT', 4)], 'name': '対象', 'default': 'ACTIVE', 'attr': 'target'}> = <_PropertyDeferred, <built-in function EnumProperty>, {'items': [('ACTIVE', 'アクティブのみ', '', 'HAND', 1), ('UP', 'アクティブより上', '', 'TRIA_UP_BAR', 2), ('DOWN', 'アクティブより下', '', 'TRIA_DOWN_BAR', 3), ('ALL', '全て', '', 'ARROW_LEFTRIGHT', 4)], 'name': '対象', 'default': 'ACTIVE', 'attr': 'target'}>
radius: <_PropertyDeferred, <built-in function FloatProperty>, {'name': '範囲倍率', 'default': 3, 'min': 0.1, 'max': 50, 'soft_min': 0.1, 'soft_max': 50, 'step': 50, 'precision': 2, 'attr': 'radius'}> = <_PropertyDeferred, <built-in function FloatProperty>, {'name': '範囲倍率', 'default': 3, 'min': 0.1, 'max': 50, 'soft_min': 0.1, 'soft_max': 50, 'step': 50, 'precision': 2, 'attr': 'radius'}>
strength: <_PropertyDeferred, <built-in function IntProperty>, {'name': '強さ', 'default': 1, 'min': 1, 'max': 10, 'soft_min': 1, 'soft_max': 10, 'attr': 'strength'}> = <_PropertyDeferred, <built-in function IntProperty>, {'name': '強さ', 'default': 1, 'min': 1, 'max': 10, 'soft_min': 1, 'soft_max': 10, 'attr': 'strength'}>
effect: <_PropertyDeferred, <built-in function EnumProperty>, {'items': [('BOTH', '増減両方', '', 'AUTOMERGE_ON', 1), ('ADD', '増加のみ', '', 'TRIA_UP', 2), ('SUB', '減少のみ', '', 'TRIA_DOWN', 3)], 'name': 'ぼかし効果', 'default': 'BOTH', 'attr': 'effect'}> = <_PropertyDeferred, <built-in function EnumProperty>, {'items': [('BOTH', '増減両方', '', 'AUTOMERGE_ON', 1), ('ADD', '増加のみ', '', 'TRIA_UP', 2), ('SUB', '減少のみ', '', 'TRIA_DOWN', 3)], 'name': 'ぼかし効果', 'default': 'BOTH', 'attr': 'effect'}>
is_normalize: <_PropertyDeferred, <built-in function BoolProperty>, {'name': '他頂点グループも調節', 'default': True, 'attr': 'is_normalize'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': '他頂点グループも調節', 'default': True, 'attr': 'is_normalize'}>
@classmethod
def poll(cls, context):
431    @classmethod
432    def poll(cls, context):
433        ob = context.active_object
434        if ob and ob.type == 'MESH':
435            return ob.vertex_groups.active
436        return False
def invoke(self, context, event):
438    def invoke(self, context, event):
439        return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
441    def draw(self, context):
442        self.layout.prop(self, 'target', icon='VIEWZOOM')
443        self.layout.prop(self, 'radius', icon='PROP_ON')
444        self.layout.prop(self, 'strength', icon='ARROW_LEFTRIGHT')
445        self.layout.prop(self, 'effect', icon='BRUSH_BLUR')
446        self.layout.prop(self, 'is_normalize', icon='GROUP')
def execute(self, context):
448    def execute(self, context):
449        ob = context.active_object
450        me = ob.data
451
452        pre_mode = ob.mode
453        bpy.ops.object.mode_set(mode='OBJECT')
454
455        bm = bmesh.new()
456        bm.from_mesh(me)
457        edge_lengths = [e.calc_length() for e in bm.edges]
458        bm.free()
459        edge_lengths.sort()
460        average_edge_length = sum(edge_lengths) / len(edge_lengths)
461        center_index = int((len(edge_lengths) - 1) / 2.0)
462        average_edge_length = (average_edge_length + edge_lengths[center_index]) / 2
463        radius = average_edge_length * self.radius
464
465        context.window_manager.progress_begin(0, len(me.vertices))
466        progress_reduce = len(me.vertices) // 200 + 1
467        near_vert_data = []
468        kd = mathutils.kdtree.KDTree(len(me.vertices))
469        for vert in me.vertices:
470            kd.insert(vert.co.copy(), vert.index)
471        kd.balance()
472        for vert in me.vertices:
473            new_vert_datum = []
474            near_vert_data.append(new_vert_datum)
475            near_vert_data_append = new_vert_datum.append
476            for co, index, dist in kd.find_range(vert.co, radius):
477                multi = (radius - dist) / radius
478                near_vert_data_append((index, multi))
479            if vert.index % progress_reduce == 0:
480                context.window_manager.progress_update(vert.index)
481        context.window_manager.progress_end()
482
483        target_vertex_groups = []
484        if self.target == 'ACTIVE':
485            target_vertex_groups.append(ob.vertex_groups.active)
486        elif self.target == 'UP':
487            for vertex_group in ob.vertex_groups:
488                if vertex_group.index <= ob.vertex_groups.active_index:
489                    target_vertex_groups.append(vertex_group)
490        elif self.target == 'DOWN':
491            for vertex_group in ob.vertex_groups:
492                if ob.vertex_groups.active_index <= vertex_group.index:
493                    target_vertex_groups.append(vertex_group)
494        elif self.target == 'ALL':
495            for vertex_group in ob.vertex_groups:
496                target_vertex_groups.append(vertex_group)
497
498        progress_total = len(target_vertex_groups) * self.strength * len(me.vertices)
499        context.window_manager.progress_begin(0, progress_total)
500        progress_reduce = progress_total // 200 + 1
501        progress_count = 0
502        for strength_count in range(self.strength):
503            for vertex_group in target_vertex_groups:
504
505                weights = []
506                weights_append = weights.append
507                for vert in me.vertices:
508                    for elem in vert.groups:
509                        if elem.group == vertex_group.index:
510                            weights_append(elem.weight)
511                            break
512                    else:
513                        weights_append(0.0)
514
515                for vert in me.vertices:
516
517                    target_weight = weights[vert.index]
518
519                    total_weight = 0.0
520                    total_multi = 0.0
521                    for index, multi in near_vert_data[vert.index]:
522                        if self.effect == 'ADD':
523                            if target_weight <= weights[index]:
524                                total_weight += weights[index] * multi
525                                total_multi += multi
526                        elif self.effect == 'SUB':
527                            if weights[index] <= target_weight:
528                                total_weight += weights[index] * multi
529                                total_multi += multi
530                        else:
531                            total_weight += weights[index] * multi
532                            total_multi += multi
533
534                    if 0 < total_multi:
535                        average_weight = total_weight / total_multi
536                    else:
537                        average_weight = 0.0
538
539                    if 0.001 < average_weight:
540                        vertex_group.add([vert.index], average_weight, 'REPLACE')
541                    else:
542                        vertex_group.remove([vert.index])
543
544                    progress_count += 1
545                    if progress_count % progress_reduce == 0:
546                        context.window_manager.progress_update(progress_count)
547
548                    if self.is_normalize:
549
550                        other_weight_total = 0.0
551                        for elem in vert.groups:
552                            if elem.group != vertex_group.index:
553                                other_weight_total += elem.weight
554
555                        diff_weight = average_weight - target_weight
556                        new_other_weight_total = other_weight_total - diff_weight
557                        if 0 < other_weight_total:
558                            other_weight_multi = new_other_weight_total / other_weight_total
559                        else:
560                            other_weight_multi = 0.0
561
562                        for elem in vert.groups:
563                            if elem.group != vertex_group.index:
564                                vg = ob.vertex_groups[elem.group]
565                                vg.add([vert.index], elem.weight * other_weight_multi, 'REPLACE')
566
567        context.window_manager.progress_end()
568        bpy.ops.object.mode_set(mode=pre_mode)
569        return {'FINISHED'}
bl_rna = <bpy_struct, Struct("OBJECT_OT_blur_vertex_group")>
Inherited Members
bpy_types.Operator
as_keywords
poll_message_set
builtins.bpy_struct
keys
values
get
pop
as_pointer
keyframe_insert
keyframe_delete
driver_add
driver_remove
is_property_set
property_unset
is_property_hidden
is_property_readonly
is_property_overridable_library
property_overridable_library_set
path_resolve
path_from_id
type_recast
bl_rna_get_subclass_py
bl_rna_get_subclass
id_properties_ensure
id_properties_clear
id_properties_ui
id_data
@compat.BlRegister()
class CNV_OT_multiply_vertex_group(bpy_types.Operator):
572@compat.BlRegister()
573class CNV_OT_multiply_vertex_group(bpy.types.Operator):
574    bl_idname = 'object.multiply_vertex_group'
575    bl_label = "頂点グループに乗算"
576    bl_description = "頂点グループのウェイトに数値を乗算し、ウェイトの強度を増減させます"
577    bl_options = {'REGISTER', 'UNDO'}
578
579    items = [
580        ('ACTIVE', "アクティブのみ", "", 'HAND', 1),
581        ('UP', "アクティブより上", "", 'TRIA_UP_BAR', 2),
582        ('DOWN', "アクティブより下", "", 'TRIA_DOWN_BAR', 3),
583        ('ALL', "全て", "", 'ARROW_LEFTRIGHT', 4),
584    ]
585    target = bpy.props.EnumProperty(items=items, name="対象", default='ACTIVE')
586    value = bpy.props.FloatProperty(name="倍率", default=1.1, min=0.1, max=10, soft_min=0.1, soft_max=10, step=10, precision=2)
587    is_normalize = bpy.props.BoolProperty(name="他頂点グループも調節", default=True)
588
589    @classmethod
590    def poll(cls, context):
591        ob = context.active_object
592        if ob:
593            if ob.type == 'MESH':
594                return ob.vertex_groups.active
595        return False
596
597    def invoke(self, context, event):
598        return context.window_manager.invoke_props_dialog(self)
599
600    def draw(self, context):
601        self.layout.prop(self, 'target', icon='VIEWZOOM')
602        self.layout.prop(self, 'value', icon='ARROW_LEFTRIGHT')
603        self.layout.prop(self, 'is_normalize', icon='GROUP')
604
605    def execute(self, context):
606        ob = context.active_object
607        me = ob.data
608
609        pre_mode = ob.mode
610        bpy.ops.object.mode_set(mode='OBJECT')
611
612        target_vertex_groups = []
613        if self.target == 'ACTIVE':
614            target_vertex_groups.append(ob.vertex_groups.active)
615        elif self.target == 'UP':
616            for vertex_group in ob.vertex_groups:
617                if vertex_group.index <= ob.vertex_groups.active_index:
618                    target_vertex_groups.append(vertex_group)
619        elif self.target == 'DOWN':
620            for vertex_group in ob.vertex_groups:
621                if ob.vertex_groups.active_index <= vertex_group.index:
622                    target_vertex_groups.append(vertex_group)
623        elif self.target == 'ALL':
624            for vertex_group in ob.vertex_groups:
625                target_vertex_groups.append(vertex_group)
626
627        for vertex_group in target_vertex_groups:
628            for vert in me.vertices:
629
630                old_weight = -1
631                other_weight_total = 0.0
632                for elem in vert.groups:
633                    if elem.group == vertex_group.index:
634                        old_weight = elem.weight
635                    else:
636                        other_weight_total += elem.weight
637                if old_weight == -1:
638                    continue
639
640                new_weight = old_weight * self.value
641                vertex_group.add([vert.index], new_weight, 'REPLACE')
642
643                if self.is_normalize:
644
645                    diff_weight = new_weight - old_weight
646
647                    new_other_weight_total = other_weight_total - diff_weight
648                    if 0 < other_weight_total:
649                        other_weight_multi = new_other_weight_total / other_weight_total
650                    else:
651                        other_weight_multi = 0.0
652
653                    for elem in vert.groups:
654                        if elem.group != vertex_group.index:
655                            vg = ob.vertex_groups[elem.group]
656                            vg.add([vert.index], elem.weight * other_weight_multi, 'REPLACE')
657
658        bpy.ops.object.mode_set(mode=pre_mode)
659        return {'FINISHED'}
bl_idname = 'object.multiply_vertex_group'
bl_label = '頂点グループに乗算'
bl_description = '頂点グループのウェイトに数値を乗算し、ウェイトの強度を増減させます'
bl_options = {'REGISTER', 'UNDO'}
items = [('ACTIVE', 'アクティブのみ', '', 'HAND', 1), ('UP', 'アクティブより上', '', 'TRIA_UP_BAR', 2), ('DOWN', 'アクティブより下', '', 'TRIA_DOWN_BAR', 3), ('ALL', '全て', '', 'ARROW_LEFTRIGHT', 4)]
target: <_PropertyDeferred, <built-in function EnumProperty>, {'items': [('ACTIVE', 'アクティブのみ', '', 'HAND', 1), ('UP', 'アクティブより上', '', 'TRIA_UP_BAR', 2), ('DOWN', 'アクティブより下', '', 'TRIA_DOWN_BAR', 3), ('ALL', '全て', '', 'ARROW_LEFTRIGHT', 4)], 'name': '対象', 'default': 'ACTIVE', 'attr': 'target'}> = <_PropertyDeferred, <built-in function EnumProperty>, {'items': [('ACTIVE', 'アクティブのみ', '', 'HAND', 1), ('UP', 'アクティブより上', '', 'TRIA_UP_BAR', 2), ('DOWN', 'アクティブより下', '', 'TRIA_DOWN_BAR', 3), ('ALL', '全て', '', 'ARROW_LEFTRIGHT', 4)], 'name': '対象', 'default': 'ACTIVE', 'attr': 'target'}>
value: <_PropertyDeferred, <built-in function FloatProperty>, {'name': '倍率', 'default': 1.1, 'min': 0.1, 'max': 10, 'soft_min': 0.1, 'soft_max': 10, 'step': 10, 'precision': 2, 'attr': 'value'}> = <_PropertyDeferred, <built-in function FloatProperty>, {'name': '倍率', 'default': 1.1, 'min': 0.1, 'max': 10, 'soft_min': 0.1, 'soft_max': 10, 'step': 10, 'precision': 2, 'attr': 'value'}>
is_normalize: <_PropertyDeferred, <built-in function BoolProperty>, {'name': '他頂点グループも調節', 'default': True, 'attr': 'is_normalize'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': '他頂点グループも調節', 'default': True, 'attr': 'is_normalize'}>
@classmethod
def poll(cls, context):
589    @classmethod
590    def poll(cls, context):
591        ob = context.active_object
592        if ob:
593            if ob.type == 'MESH':
594                return ob.vertex_groups.active
595        return False
def invoke(self, context, event):
597    def invoke(self, context, event):
598        return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
600    def draw(self, context):
601        self.layout.prop(self, 'target', icon='VIEWZOOM')
602        self.layout.prop(self, 'value', icon='ARROW_LEFTRIGHT')
603        self.layout.prop(self, 'is_normalize', icon='GROUP')
def execute(self, context):
605    def execute(self, context):
606        ob = context.active_object
607        me = ob.data
608
609        pre_mode = ob.mode
610        bpy.ops.object.mode_set(mode='OBJECT')
611
612        target_vertex_groups = []
613        if self.target == 'ACTIVE':
614            target_vertex_groups.append(ob.vertex_groups.active)
615        elif self.target == 'UP':
616            for vertex_group in ob.vertex_groups:
617                if vertex_group.index <= ob.vertex_groups.active_index:
618                    target_vertex_groups.append(vertex_group)
619        elif self.target == 'DOWN':
620            for vertex_group in ob.vertex_groups:
621                if ob.vertex_groups.active_index <= vertex_group.index:
622                    target_vertex_groups.append(vertex_group)
623        elif self.target == 'ALL':
624            for vertex_group in ob.vertex_groups:
625                target_vertex_groups.append(vertex_group)
626
627        for vertex_group in target_vertex_groups:
628            for vert in me.vertices:
629
630                old_weight = -1
631                other_weight_total = 0.0
632                for elem in vert.groups:
633                    if elem.group == vertex_group.index:
634                        old_weight = elem.weight
635                    else:
636                        other_weight_total += elem.weight
637                if old_weight == -1:
638                    continue
639
640                new_weight = old_weight * self.value
641                vertex_group.add([vert.index], new_weight, 'REPLACE')
642
643                if self.is_normalize:
644
645                    diff_weight = new_weight - old_weight
646
647                    new_other_weight_total = other_weight_total - diff_weight
648                    if 0 < other_weight_total:
649                        other_weight_multi = new_other_weight_total / other_weight_total
650                    else:
651                        other_weight_multi = 0.0
652
653                    for elem in vert.groups:
654                        if elem.group != vertex_group.index:
655                            vg = ob.vertex_groups[elem.group]
656                            vg.add([vert.index], elem.weight * other_weight_multi, 'REPLACE')
657
658        bpy.ops.object.mode_set(mode=pre_mode)
659        return {'FINISHED'}
bl_rna = <bpy_struct, Struct("OBJECT_OT_multiply_vertex_group")>
Inherited Members
bpy_types.Operator
as_keywords
poll_message_set
builtins.bpy_struct
keys
values
get
pop
as_pointer
keyframe_insert
keyframe_delete
driver_add
driver_remove
is_property_set
property_unset
is_property_hidden
is_property_readonly
is_property_overridable_library
property_overridable_library_set
path_resolve
path_from_id
type_recast
bl_rna_get_subclass_py
bl_rna_get_subclass
id_properties_ensure
id_properties_clear
id_properties_ui
id_data
@compat.BlRegister()
class CNV_OT_remove_noassign_vertex_groups(bpy_types.Operator):
662@compat.BlRegister()
663class CNV_OT_remove_noassign_vertex_groups(bpy.types.Operator):
664    bl_idname = 'object.remove_noassign_vertex_groups'
665    bl_label = "割り当てのない頂点グループを削除"
666    bl_description = "どの頂点にも割り当てられていない頂点グループを全て削除します"
667    bl_options = {'REGISTER', 'UNDO'}
668
669    threshold = bpy.props.FloatProperty(name="これ以下の影響は切り捨て", default=0.000001, min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, step=1, precision=10)
670
671    @classmethod
672    def poll(cls, context):
673        ob = context.active_object
674        if ob and ob.type == 'MESH':
675            return len(ob.vertex_groups) > 0
676        return False
677
678    def invoke(self, context, event):
679        return context.window_manager.invoke_props_dialog(self)
680
681    def draw(self, context):
682        self.layout.prop(self, 'threshold')
683
684    def execute(self, context):
685        ob = context.active_object
686        me = ob.data
687
688        is_keeps = [False for i in range(len(ob.vertex_groups))]
689
690        for vert in me.vertices:
691            for vge in vert.groups:
692                if not is_keeps[vge.group]:
693                    if self.threshold < vge.weight:
694                        is_keeps[vge.group] = True
695
696        copy_vertex_groups = ob.vertex_groups[:]
697        for i in range(len(copy_vertex_groups)):
698            if not is_keeps[i] and not copy_vertex_groups[i].lock_weight:
699                ob.vertex_groups.remove(copy_vertex_groups[i])
700
701        return {'FINISHED'}
bl_idname = 'object.remove_noassign_vertex_groups'
bl_label = '割り当てのない頂点グループを削除'
bl_description = 'どの頂点にも割り当てられていない頂点グループを全て削除します'
bl_options = {'REGISTER', 'UNDO'}
threshold: <_PropertyDeferred, <built-in function FloatProperty>, {'name': 'これ以下の影響は切り捨て', 'default': 1e-06, 'min': 0.0, 'max': 1.0, 'soft_min': 0.0, 'soft_max': 1.0, 'step': 1, 'precision': 10, 'attr': 'threshold'}> = <_PropertyDeferred, <built-in function FloatProperty>, {'name': 'これ以下の影響は切り捨て', 'default': 1e-06, 'min': 0.0, 'max': 1.0, 'soft_min': 0.0, 'soft_max': 1.0, 'step': 1, 'precision': 10, 'attr': 'threshold'}>
@classmethod
def poll(cls, context):
671    @classmethod
672    def poll(cls, context):
673        ob = context.active_object
674        if ob and ob.type == 'MESH':
675            return len(ob.vertex_groups) > 0
676        return False
def invoke(self, context, event):
678    def invoke(self, context, event):
679        return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
681    def draw(self, context):
682        self.layout.prop(self, 'threshold')
def execute(self, context):
684    def execute(self, context):
685        ob = context.active_object
686        me = ob.data
687
688        is_keeps = [False for i in range(len(ob.vertex_groups))]
689
690        for vert in me.vertices:
691            for vge in vert.groups:
692                if not is_keeps[vge.group]:
693                    if self.threshold < vge.weight:
694                        is_keeps[vge.group] = True
695
696        copy_vertex_groups = ob.vertex_groups[:]
697        for i in range(len(copy_vertex_groups)):
698            if not is_keeps[i] and not copy_vertex_groups[i].lock_weight:
699                ob.vertex_groups.remove(copy_vertex_groups[i])
700
701        return {'FINISHED'}
bl_rna = <bpy_struct, Struct("OBJECT_OT_remove_noassign_vertex_groups")>
Inherited Members
bpy_types.Operator
as_keywords
poll_message_set
builtins.bpy_struct
keys
values
items
get
pop
as_pointer
keyframe_insert
keyframe_delete
driver_add
driver_remove
is_property_set
property_unset
is_property_hidden
is_property_readonly
is_property_overridable_library
property_overridable_library_set
path_resolve
path_from_id
type_recast
bl_rna_get_subclass_py
bl_rna_get_subclass
id_properties_ensure
id_properties_clear
id_properties_ui
id_data