Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'rigify/utils/mechanism.py')
-rw-r--r--rigify/utils/mechanism.py152
1 files changed, 97 insertions, 55 deletions
diff --git a/rigify/utils/mechanism.py b/rigify/utils/mechanism.py
index ac5f8790..53c9cedb 100644
--- a/rigify/utils/mechanism.py
+++ b/rigify/utils/mechanism.py
@@ -3,31 +3,49 @@
import bpy
import re
-from bpy.types import bpy_prop_collection, Material
+from typing import TYPE_CHECKING, Optional, Any, Collection
+
+from bpy.types import (bpy_prop_collection, Material, Object, PoseBone, Driver, FCurve,
+ DriverTarget, ID, bpy_struct, FModifierGenerator, Constraint, AnimData,
+ ArmatureConstraint)
from rna_prop_ui import rna_idprop_ui_create
from rna_prop_ui import rna_idprop_quote_path as quote_property
-from .misc import force_lazy
+from .misc import force_lazy, ArmatureObject, Lazy
+
+if TYPE_CHECKING:
+ from ..base_rig import BaseRig
+
-#=============================================
+##############################################
# Constraint creation utilities
-#=============================================
+##############################################
-_TRACK_AXIS_MAP = {
+_TRACK_AXIS_MAP = {
'X': 'TRACK_X', '-X': 'TRACK_NEGATIVE_X',
'Y': 'TRACK_Y', '-Y': 'TRACK_NEGATIVE_Y',
'Z': 'TRACK_Z', '-Z': 'TRACK_NEGATIVE_Z',
}
+
def _set_default_attr(obj, options, attr, value):
if hasattr(obj, attr):
options.setdefault(attr, value)
+
def make_constraint(
- owner, con_type, target=None, subtarget=None, *, insert_index=None,
- space=None, track_axis=None, use_xyz=None, use_limit_xyz=None, invert_xyz=None,
- targets=None, **options):
+ owner: Object | PoseBone, con_type: str,
+ target: Optional[Object] = None,
+ subtarget: Optional[str] = None, *,
+ insert_index: Optional[int] = None,
+ space: Optional[str] = None,
+ track_axis: Optional[str] = None,
+ use_xyz: Optional[Collection[bool]] = None,
+ use_limit_xyz: Optional[Collection[bool]] = None,
+ invert_xyz: Optional[Collection[bool]] = None,
+ targets: list[Lazy[str | tuple | dict]] = None,
+ **options):
"""
Creates and initializes constraint of the specified type for the owner bone.
@@ -52,7 +70,7 @@ def make_constraint(
# For Armature constraints, allow passing a "targets" list as a keyword argument.
if targets is not None:
- assert con.type == 'ARMATURE'
+ assert isinstance(con, ArmatureConstraint)
for target_info in targets:
con_target = con.targets.new()
con_target.target = owner.id_data
@@ -66,6 +84,7 @@ def make_constraint(
else:
con_target.target, con_target.subtarget, con_target.weight = map(force_lazy, target_info)
else:
+ assert isinstance(target_info, dict)
for key, val in target_info.items():
setattr(con_target, key, force_lazy(val))
@@ -104,13 +123,15 @@ def make_constraint(
return con
-#=============================================
+
+##############################################
# Custom property creation utilities
-#=============================================
+##############################################
+# noinspection PyShadowingBuiltins
def make_property(
- owner, name, default, *, min=0.0, max=1.0, soft_min=None, soft_max=None,
- description=None, overridable=True, **options):
+ owner, name: str, default, *, min=0.0, max=1.0, soft_min=None, soft_max=None,
+ description: Optional[str] = None, overridable=True, **options):
"""
Creates and initializes a custom property of owner.
@@ -120,18 +141,19 @@ def make_property(
# Some keyword argument defaults differ
rna_idprop_ui_create(
- owner, name, default = default,
- min = min, max = max, soft_min = soft_min, soft_max = soft_max,
- description = description or name,
- overridable = overridable,
+ owner, name, default=default,
+ min=min, max=max, soft_min=soft_min, soft_max=soft_max,
+ description=description or name,
+ overridable=overridable,
**options
)
-#=============================================
+
+##############################################
# Driver creation utilities
-#=============================================
+##############################################
-def _init_driver_target(drv_target, var_info, target_id):
+def _init_driver_target(drv_target: DriverTarget, var_info, target_id: Optional[ID]):
"""Initialize a driver variable target from a specification."""
# Parse the simple list format for the common case.
@@ -140,9 +162,9 @@ def _init_driver_target(drv_target, var_info, target_id):
# If target_id is supplied as parameter, allow omitting it
if target_id is None or isinstance(var_info[0], bpy.types.ID):
- target_id,subtarget,*refs = var_info
+ target_id, subtarget, *refs = var_info
else:
- subtarget,*refs = var_info
+ subtarget, *refs = var_info
subtarget = force_lazy(subtarget)
@@ -165,7 +187,7 @@ def _init_driver_target(drv_target, var_info, target_id):
if isinstance(item, str):
path += item if item[0] == '.' else quote_property(item)
else:
- path += '[%r]' % (item)
+ path += f'[{repr(item)}]'
if path[0] == '.':
path = path[1:]
@@ -184,7 +206,7 @@ def _init_driver_target(drv_target, var_info, target_id):
setattr(drv_target, tp, force_lazy(tv))
-def _add_driver_variable(drv, var_name, var_info, target_id):
+def _add_driver_variable(drv: Driver, var_name: str, var_info, target_id: Optional[ID]):
"""Add and initialize a driver variable."""
var = drv.variables.new()
@@ -209,7 +231,13 @@ def _add_driver_variable(drv, var_name, var_info, target_id):
elif p != 'type':
setattr(var, p, force_lazy(v))
-def make_driver(owner, prop, *, index=-1, type='SUM', expression=None, variables={}, polynomial=None, target_id=None):
+
+# noinspection PyIncorrectDocstring,PyShadowingBuiltins,PyDefaultArgument
+def make_driver(owner: bpy_struct, prop: str, *, index=-1, type='SUM',
+ expression: Optional[str] = None,
+ variables: list | dict = {},
+ polynomial: Optional[list[float]] = None,
+ target_id: Optional[ID] = None) -> FCurve:
"""
Creates and initializes a driver for the 'prop' property of owner.
@@ -222,7 +250,7 @@ def make_driver(owner, prop, *, index=-1, type='SUM', expression=None, variables
Specification format:
If the variables argument is a dictionary, keys specify variable names.
- Otherwise names are set to var, var1, var2, ... etc:
+ Otherwise, names are set to var, var1, var2, ... etc:
variables = [ ..., ..., ... ]
variables = { 'var': ..., 'var1': ..., 'var2': ... }
@@ -288,20 +316,22 @@ def make_driver(owner, prop, *, index=-1, type='SUM', expression=None, variables
if polynomial is not None:
drv_modifier = fcu.modifiers.new('GENERATOR')
+ assert isinstance(drv_modifier, FModifierGenerator)
drv_modifier.mode = 'POLYNOMIAL'
drv_modifier.poly_order = len(polynomial)-1
- for i,v in enumerate(polynomial):
+ for i, v in enumerate(polynomial):
drv_modifier.coefficients[i] = v
return fcu
-#=============================================
+##############################################
# Driver variable utilities
-#=============================================
-
+##############################################
-def driver_var_transform(target, bone=None, *, type='LOC_X', space='WORLD', rotation_mode='AUTO'):
+# noinspection PyShadowingBuiltins
+def driver_var_transform(target: ID, bone: Optional[str] = None, *,
+ type='LOC_X', space='WORLD', rotation_mode='AUTO'):
"""
Create a Transform Channel driver variable specification.
@@ -323,10 +353,14 @@ def driver_var_transform(target, bone=None, *, type='LOC_X', space='WORLD', rota
if bone is not None:
target_map['bone_target'] = bone
- return { 'type': 'TRANSFORMS', 'targets': [ target_map ] }
+ return {'type': 'TRANSFORMS', 'targets': [target_map]}
-def driver_var_distance(target, *, bone1=None, target2=None, bone2=None, space1='WORLD', space2='WORLD'):
+def driver_var_distance(target: ID, *,
+ bone1: Optional[str] = None,
+ target2: Optional[ID] = None,
+ bone2: Optional[str] = None,
+ space1='WORLD', space2='WORLD'):
"""
Create a Distance driver variable specification.
@@ -358,11 +392,11 @@ def driver_var_distance(target, *, bone1=None, target2=None, bone2=None, space1=
return {'type': 'LOC_DIFF', 'targets': [target1_map, target2_map]}
-#=============================================
+##############################################
# Constraint management
-#=============================================
+##############################################
-def move_constraint(source, target, con):
+def move_constraint(source: Object | PoseBone, target: Object | PoseBone | str, con: Constraint):
"""
Move a constraint from one owner to another, together with drivers.
"""
@@ -385,7 +419,11 @@ def move_constraint(source, target, con):
source.constraints.remove(con)
-def move_all_constraints(obj, source, target, *, prefix=''):
+
+def move_all_constraints(obj: Object,
+ source: Object | PoseBone | str,
+ target: Object | PoseBone | str, *,
+ prefix=''):
"""
Move all constraints with the specified name prefix from one bone to another.
"""
@@ -400,11 +438,11 @@ def move_all_constraints(obj, source, target, *, prefix=''):
move_constraint(source, target, con)
-#=============================================
+##############################################
# Custom property management
-#=============================================
+##############################################
-def deactivate_custom_properties(obj, *, reset=True):
+def deactivate_custom_properties(obj: bpy_struct, *, reset=True):
"""Disable drivers on custom properties and reset values to default."""
prefix = '["'
@@ -420,14 +458,14 @@ def deactivate_custom_properties(obj, *, reset=True):
if reset:
for key, value in obj.items():
- valtype = type(value)
- if valtype in {int, float}:
+ val_type = type(value)
+ if val_type in {int, float}:
ui_data = obj.id_properties_ui(key)
rna_data = ui_data.as_dict()
- obj[key] = valtype(rna_data.get("default", 0))
+ obj[key] = val_type(rna_data.get("default", 0))
-def reactivate_custom_properties(obj):
+def reactivate_custom_properties(obj: bpy_struct):
"""Re-enable drivers on custom properties."""
prefix = '["'
@@ -442,7 +480,8 @@ def reactivate_custom_properties(obj):
fcu.mute = False
-def copy_custom_properties(src, dest, *, prefix='', dest_prefix='', link_driver=False, overridable=True):
+def copy_custom_properties(src, dest, *, prefix='', dest_prefix='',
+ link_driver=False, overridable=True) -> list[tuple[str, str, Any]]:
"""Copy custom properties with filtering by prefix. Optionally link using drivers."""
res = []
@@ -456,7 +495,7 @@ def copy_custom_properties(src, dest, *, prefix='', dest_prefix='', link_driver=
try:
ui_data_src = src.id_properties_ui(key)
except TypeError:
- # Some property types, eg. Python dictionaries
+ # Some property types, e.g. Python dictionaries
# don't support id_properties_ui.
continue
@@ -476,18 +515,18 @@ def copy_custom_properties(src, dest, *, prefix='', dest_prefix='', link_driver=
return res
-def copy_custom_properties_with_ui(rig, src, dest_bone, *, ui_controls=None, **options):
+def copy_custom_properties_with_ui(rig: 'BaseRig', src, dest_bone, *, ui_controls=None, **options):
"""Copy custom properties, and create rig UI for them."""
if isinstance(src, str):
src = rig.get_bone(src)
- bone = rig.get_bone(dest_bone)
+ bone: PoseBone = rig.get_bone(dest_bone)
mapping = copy_custom_properties(src, bone, **options)
if mapping:
panel = rig.script.panel_with_selected_check(rig, ui_controls or rig.bones.flatten('ctrl'))
- for key,new_key,value in sorted(mapping, key=lambda item: item[1]):
+ for key, new_key, value in sorted(mapping, key=lambda item: item[1]):
name = new_key
# Replace delimiters with spaces
@@ -508,15 +547,15 @@ def copy_custom_properties_with_ui(rig, src, dest_bone, *, ui_controls=None, **o
return mapping
-#=============================================
+##############################################
# Driver management
-#=============================================
+##############################################
def refresh_drivers(obj):
"""Cause all drivers belonging to the object to be re-evaluated, clearing any errors."""
# Refresh object's own drivers if any
- anim_data = getattr(obj, 'animation_data', None)
+ anim_data: Optional[AnimData] = getattr(obj, 'animation_data', None)
if anim_data:
for fcu in anim_data.drivers:
@@ -531,7 +570,7 @@ def refresh_drivers(obj):
def refresh_all_drivers():
"""Cause all drivers in the file to be re-evaluated, clearing any errors."""
- # Iterate over all datablocks in the file
+ # Iterate over all data blocks in the file
for attr in dir(bpy.data):
coll = getattr(bpy.data, attr, None)
@@ -540,11 +579,13 @@ def refresh_all_drivers():
refresh_drivers(item)
-#=============================================
+##############################################
# Utility mixin
-#=============================================
+##############################################
class MechanismUtilityMixin(object):
+ obj: ArmatureObject
+
"""
Provides methods for more convenient creation of constraints, properties
and drivers within an armature (by implicitly providing context).
@@ -552,6 +593,7 @@ class MechanismUtilityMixin(object):
Requires self.obj to be the armature object being worked on.
"""
+ # noinspection PyShadowingBuiltins
def make_constraint(self, bone, type, subtarget=None, **args):
assert(self.obj.mode == 'OBJECT')
return make_constraint(self.obj.pose.bones[bone], type, self.obj, subtarget, **args)