Module kawa_scripts.vertex_groups
Useful tools for Vertex Groups
Expand source code
# Kawashirov's Scripts (c) 2021 by Sergey V. Kawashirov
#
# Kawashirov's Scripts is licensed under a
# Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
#
# You should have received a copy of the license along with this
# work. If not, see <http://creativecommons.org/licenses/by-nc-sa/3.0/>.
#
#
"""
Useful tools for Vertex Groups
"""
import bpy as _bpy
from bpy import context as _C
from ._internals import log as _log
from ._internals import KawaOperator as _KawaOperator
from . import _doc
import typing as _typing
if _typing.TYPE_CHECKING:
from typing import *
from bpy.types import *
def _any_weight(mesh: 'Mesh', group_index: 'int', limit: 'float' = 0.0):
# В худшем случае производительность NxM, но иначе никак:
# VertexGroup.weight(index) требует теста
for i in range(len(mesh.vertices)):
for vge in mesh.vertices[i].groups.values():
if vge.group == group_index and vge.weight > limit:
return True
return False
def remove_empty(objs: 'Iterable[Object]', limit: 'float' = 0.0, ignore_locked: 'bool' = False, op: 'Operator' = None) -> 'Tuple[int, int]':
"""
**Удаляет пустые Vertex Groups с указанных объектов.**
Если режим объекта не `OBJECT`, то он игнорируется с предупреждением.
Такой оператор уже еть в CATS, но кое-кто попросил меня сделать его отдельно т.к.
*"Срашно, очень страшно пользоваться CATSом, опять поломает мне всю модельку."*
Vertex Group считается пустым, если все его веса вершин имеют значение меньше либо равное `limit`.
Если `limit` меньше нуля, то тогда любой (даже нулевой) вес считается не-пустым.
Если `ignore_locked`, то заблокированные группы (с флагом lock_weight) не будут удаляться.
Возвращает: `(removed_groups, removed_objects)`, где
`removed_groups` - сколько групп было удалено,
`removed_objects` - из скольки объектов было удалено.
Доступен из UI как оператор `kawa.vertex_group_remove_empty`
"""
removed_groups, removed_objects = 0, 0
for obj in objs:
if obj.type != 'MESH':
continue
if obj.vertex_groups is None or len(obj.vertex_groups) < 1:
continue
if obj.mode != 'OBJECT':
_log.warning('{0} is in {1} mode, ignored.'.format(repr(obj), repr(obj.mode)), op=op)
continue
mesh = obj.data # type: Mesh
removed = 0
for group in list(obj.vertex_groups.values()):
if ignore_locked and group.lock_weight:
continue
if not _any_weight(mesh, group.index, limit):
obj.vertex_groups.remove(group)
removed += 1
if removed > 0:
removed_groups += removed
removed_objects += 1
return removed_groups, removed_objects
class OperatorRemoveEmpty(_KawaOperator):
"""
Operator of `remove_empty`.
"""
bl_idname = "kawa.vertex_group_remove_empty"
bl_label = "Remove Empty Vertex Groups"
bl_options = {'REGISTER', 'UNDO'}
limit: _bpy.props.FloatProperty(
name="Limit",
description="Do not count vertices which weight is below or equal to this limit. Count everything if less than zero.",
min=-1e-05,
default=0.0,
max=1.0,
precision=6,
subtype='FACTOR',
)
ignore_locked: _bpy.props.BoolProperty(
name="Ignore Locked",
description="Do not remove locked (with flag lock_weight) groups.",
default=False,
)
@classmethod
def poll(cls, context: 'Context'):
if context.mode != 'OBJECT':
return False # Требуется режим OBJECT
selected = cls.get_selected_objs(context)
if not any(True for obj in selected if obj.type == 'MESH'):
return False # Должны быть выбраны какие-то Меш-объекты
return True
def invoke(self, context: 'Context', event):
return context.window_manager.invoke_props_dialog(self)
def execute(self, context: 'Context'):
removed_groups, removed_objects = remove_empty(self.get_selected_objs(context),
limit=self.limit, ignore_locked=self.ignore_locked, op=self)
_log.info("Removed {0} vertex groups from {1} objects.".format(removed_groups, removed_objects), op=self)
return {'FINISHED'} if removed_groups > 0 else {'CANCELLED'}
classes = (
OperatorRemoveEmpty,
)
__pdoc__ = dict()
_doc.process_blender_classes(__pdoc__, classes)
Functions
def remove_empty(objs: Iterable[Object], limit: float = 0.0, ignore_locked: bool = False, op: Operator = None) ‑> Tuple[int, int]
-
Удаляет пустые Vertex Groups с указанных объектов. Если режим объекта не
OBJECT
, то он игнорируется с предупреждением. Такой оператор уже еть в CATS, но кое-кто попросил меня сделать его отдельно т.к. "Срашно, очень страшно пользоваться CATSом, опять поломает мне всю модельку." Vertex Group считается пустым, если все его веса вершин имеют значение меньше либо равноеlimit
. Еслиlimit
меньше нуля, то тогда любой (даже нулевой) вес считается не-пустым. Еслиignore_locked
, то заблокированные группы (с флагом lock_weight) не будут удаляться.Возвращает:
(removed_groups, removed_objects)
, гдеremoved_groups
- сколько групп было удалено,removed_objects
- из скольки объектов было удалено.Доступен из UI как оператор
kawa.vertex_group_remove_empty
Expand source code
def remove_empty(objs: 'Iterable[Object]', limit: 'float' = 0.0, ignore_locked: 'bool' = False, op: 'Operator' = None) -> 'Tuple[int, int]': """ **Удаляет пустые Vertex Groups с указанных объектов.** Если режим объекта не `OBJECT`, то он игнорируется с предупреждением. Такой оператор уже еть в CATS, но кое-кто попросил меня сделать его отдельно т.к. *"Срашно, очень страшно пользоваться CATSом, опять поломает мне всю модельку."* Vertex Group считается пустым, если все его веса вершин имеют значение меньше либо равное `limit`. Если `limit` меньше нуля, то тогда любой (даже нулевой) вес считается не-пустым. Если `ignore_locked`, то заблокированные группы (с флагом lock_weight) не будут удаляться. Возвращает: `(removed_groups, removed_objects)`, где `removed_groups` - сколько групп было удалено, `removed_objects` - из скольки объектов было удалено. Доступен из UI как оператор `kawa.vertex_group_remove_empty` """ removed_groups, removed_objects = 0, 0 for obj in objs: if obj.type != 'MESH': continue if obj.vertex_groups is None or len(obj.vertex_groups) < 1: continue if obj.mode != 'OBJECT': _log.warning('{0} is in {1} mode, ignored.'.format(repr(obj), repr(obj.mode)), op=op) continue mesh = obj.data # type: Mesh removed = 0 for group in list(obj.vertex_groups.values()): if ignore_locked and group.lock_weight: continue if not _any_weight(mesh, group.index, limit): obj.vertex_groups.remove(group) removed += 1 if removed > 0: removed_groups += removed removed_objects += 1 return removed_groups, removed_objects
Classes
class OperatorRemoveEmpty
-
Operator of
remove_empty()
.ID name:
kawa.vertex_group_remove_empty
.Label:
Remove Empty Vertex Groups
.Expand source code
class OperatorRemoveEmpty(_KawaOperator): """ Operator of `remove_empty`. """ bl_idname = "kawa.vertex_group_remove_empty" bl_label = "Remove Empty Vertex Groups" bl_options = {'REGISTER', 'UNDO'} limit: _bpy.props.FloatProperty( name="Limit", description="Do not count vertices which weight is below or equal to this limit. Count everything if less than zero.", min=-1e-05, default=0.0, max=1.0, precision=6, subtype='FACTOR', ) ignore_locked: _bpy.props.BoolProperty( name="Ignore Locked", description="Do not remove locked (with flag lock_weight) groups.", default=False, ) @classmethod def poll(cls, context: 'Context'): if context.mode != 'OBJECT': return False # Требуется режим OBJECT selected = cls.get_selected_objs(context) if not any(True for obj in selected if obj.type == 'MESH'): return False # Должны быть выбраны какие-то Меш-объекты return True def invoke(self, context: 'Context', event): return context.window_manager.invoke_props_dialog(self) def execute(self, context: 'Context'): removed_groups, removed_objects = remove_empty(self.get_selected_objs(context), limit=self.limit, ignore_locked=self.ignore_locked, op=self) _log.info("Removed {0} vertex groups from {1} objects.".format(removed_groups, removed_objects), op=self) return {'FINISHED'} if removed_groups > 0 else {'CANCELLED'}
Ancestors
- kawa_scripts._internals.KawaOperator
- bpy.types.Operator
- bpy.types.bpy_struct
Class variables
var ignore_locked : None
var limit : None