blender/scripts/modules/keyingsets_utils.py
Campbell Barton 6ca1417103 Cleanup: suppress unused Python warnings
Suppress unused warnings using the "vulture" utility.

- Include public definitions in the modules `__all__`.
- Mark arguments & variables as unused with a "_" prefix.
2024-12-03 12:54:13 +11:00

290 lines
8.7 KiB
Python

# SPDX-FileCopyrightText: 2010-2023 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
# This file defines a set of methods that are useful for various
# Relative Keying Set (RKS) related operations, such as: callbacks
# for polling, iterator callbacks, and also generate callbacks.
# All of these can be used in conjunction with the others.
__all__ = (
"path_add_property",
"RKS_POLL_selected_objects",
"RKS_POLL_selected_bones",
"RKS_POLL_selected_items",
"RKS_ITER_selected_objects",
"RKS_ITER_selected_bones",
"RKS_ITER_selected_item",
"RKS_GEN_available",
"RKS_GEN_custom_props",
"RKS_GEN_location",
"RKS_GEN_rotation",
"RKS_GEN_scaling",
"RKS_GEN_bendy_bones",
)
import bpy
###########################
# General Utilities
# Append the specified property name on the existing path.
def path_add_property(path, prop):
if path:
return path + "." + prop
else:
return prop
###########################
# Poll Callbacks
# selected objects (active object must be in object mode)
def RKS_POLL_selected_objects(_ksi, context):
ob = context.active_object
if ob:
return ob.mode == 'OBJECT'
else:
return bool(context.selected_objects)
# selected bones
def RKS_POLL_selected_bones(_ksi, context):
# we must be in Pose Mode, and there must be some bones selected
ob = context.active_object
if ob and ob.mode == 'POSE':
if context.active_pose_bone or context.selected_pose_bones:
return True
# nothing selected
return False
# selected bones or objects
def RKS_POLL_selected_items(ksi, context):
return (RKS_POLL_selected_bones(ksi, context) or
RKS_POLL_selected_objects(ksi, context))
###########################
# Iterator Callbacks
# All selected objects or pose bones, depending on which we've got.
def RKS_ITER_selected_item(ksi, context, ks):
ob = context.active_object
if ob and ob.mode == 'POSE':
for bone in context.selected_pose_bones:
ksi.generate(context, ks, bone)
else:
for ob in context.selected_objects:
ksi.generate(context, ks, ob)
# All selected objects only.
def RKS_ITER_selected_objects(ksi, context, ks):
for ob in context.selected_objects:
ksi.generate(context, ks, ob)
# All selected bones only.
def RKS_ITER_selected_bones(ksi, context, ks):
for bone in context.selected_pose_bones:
ksi.generate(context, ks, bone)
###########################
# Generate Callbacks
# "Available" F-Curves.
def RKS_GEN_available(_ksi, _context, ks, data):
# try to get the animation data associated with the closest
# ID-block to the data (neither of which may exist/be easy to find)
id_block = data.id_data
adt = getattr(id_block, "animation_data", None)
# there must also be an active action...
if adt is None or adt.action is None:
return
# if we haven't got an ID-block as 'data', try to restrict
# paths added to only those which branch off from here
# i.e. for bones
if id_block != data:
basePath = data.path_from_id()
else:
basePath = None # this is not needed...
# for each F-Curve, include a path to key it
# NOTE: we don't need to set the group settings here
for fcu in adt.action.fcurves:
if basePath:
if basePath in fcu.data_path:
ks.paths.add(id_block, fcu.data_path, index=fcu.array_index)
else:
ks.paths.add(id_block, fcu.data_path, index=fcu.array_index)
# ------
# get ID block and based ID path for transform generators
# private function
def get_transform_generators_base_info(data):
# ID-block for the data
id_block = data.id_data
# get base path and grouping method/name
if isinstance(data, bpy.types.ID):
# no path in this case
path = ""
# transform data on ID-blocks directly should get grouped under a
# hardcoded label ("Object Transforms") so that they get grouped
# consistently when keyframed directly
grouping = "Object Transforms"
else:
# get the path to the ID-block
path = data.path_from_id()
# try to use the name of the data element to group the F-Curve
# else fallback on the KeyingSet name
grouping = getattr(data, "name", None)
# return the ID-block and the path
return id_block, path, grouping
# Location
def RKS_GEN_location(_ksi, _context, ks, data):
# get id-block and path info
id_block, base_path, grouping = get_transform_generators_base_info(data)
# add the property name to the base path
path = path_add_property(base_path, "location")
# add Keying Set entry for this...
if grouping:
ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
else:
ks.paths.add(id_block, path)
# Rotation
def RKS_GEN_rotation(_ksi, _context, ks, data):
# get id-block and path info
id_block, base_path, grouping = get_transform_generators_base_info(data)
# add the property name to the base path
# rotation mode affects the property used
if data.rotation_mode == 'QUATERNION':
path = path_add_property(base_path, "rotation_quaternion")
elif data.rotation_mode == 'AXIS_ANGLE':
path = path_add_property(base_path, "rotation_axis_angle")
else:
path = path_add_property(base_path, "rotation_euler")
# add Keying Set entry for this...
if grouping:
ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
else:
ks.paths.add(id_block, path)
# Scaling
def RKS_GEN_scaling(_ksi, _context, ks, data):
# get id-block and path info
id_block, base_path, grouping = get_transform_generators_base_info(data)
# add the property name to the base path
path = path_add_property(base_path, "scale")
# add Keying Set entry for this...
if grouping:
ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
else:
ks.paths.add(id_block, path)
# Custom Properties
def RKS_GEN_custom_props(_ksi, _context, ks, data):
# get id-block and path info
id_block, base_path, grouping = get_transform_generators_base_info(data)
# Only some RNA types can be animated.
prop_type_compat = {bpy.types.BoolProperty,
bpy.types.IntProperty,
bpy.types.FloatProperty,
bpy.types.EnumProperty}
# When working with a pose, 'id_block' is the armature object (which should
# get the animation data), whereas 'data' is the bone being keyed.
for cprop_name in data.keys():
# ignore special "_RNA_UI" used for UI editing
if cprop_name == "_RNA_UI":
continue
if cprop_name in data.bl_rna.properties and not data.bl_rna.properties[cprop_name].is_animatable:
continue
if cprop_name in data.bl_rna.properties:
prop_path = cprop_name
else:
prop_path = '["{:s}"]'.format(bpy.utils.escape_identifier(cprop_name))
try:
rna_property = data.path_resolve(prop_path, False)
except ValueError:
# Can technically happen, but there is no known case.
continue
if rna_property is None:
# In this case the property cannot be converted to an
# FCurve-compatible value, so we can't keyframe it anyways.
continue
if rna_property.rna_type not in prop_type_compat:
continue
path = "{:s}{:s}".format(base_path, prop_path)
if grouping:
ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
else:
ks.paths.add(id_block, path)
# ------
# Property identifiers for Bendy Bones
bbone_property_ids = (
"bbone_curveinx",
"bbone_curveiny",
"bbone_curveoutx",
"bbone_curveouty",
"bbone_rollin",
"bbone_rollout",
"bbone_scalein",
"bbone_scaleout",
# NOTE: These are in the nested bone struct
# Do it this way to force them to be included
# in whatever actions are being keyed here
"bone.bbone_in",
"bone.bbone_out",
)
# Add Keying Set entries for bendy bones
def RKS_GEN_bendy_bones(_ksi, _context, ks, data):
# get id-block and path info
# NOTE: This assumes that we're dealing with a bone here...
id_block, base_path, grouping = get_transform_generators_base_info(data)
# for each of the bendy bone properties, add a Keying Set entry for it...
for propname in bbone_property_ids:
# add the property name to the base path
path = path_add_property(base_path, propname)
# add Keying Set entry for this...
if grouping:
ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
else:
ks.paths.add(id_block, path)