diff options
Diffstat (limited to 'pose_library/pose_usage.py')
-rw-r--r-- | pose_library/pose_usage.py | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/pose_library/pose_usage.py b/pose_library/pose_usage.py new file mode 100644 index 00000000..dc496d9c --- /dev/null +++ b/pose_library/pose_usage.py @@ -0,0 +1,185 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +""" +Pose Library - usage functions. +""" + +from typing import Set +import re + +from bpy.types import ( + Action, + Object, +) + + +def select_bones(arm_object: Object, action: Action, *, select: bool, flipped: bool) -> None: + pose_bone_re = re.compile(r'pose.bones\["([^"]+)"\]') + pose = arm_object.pose + + seen_bone_names: Set[str] = set() + + for fcurve in action.fcurves: + data_path: str = fcurve.data_path + match = pose_bone_re.match(data_path) + if not match: + continue + + bone_name = match.group(1) + + if bone_name in seen_bone_names: + continue + seen_bone_names.add(bone_name) + + if flipped: + bone_name = flip_side_name(bone_name) + + try: + pose_bone = pose.bones[bone_name] + except KeyError: + continue + + pose_bone.bone.select = select + + +_FLIP_SEPARATORS = set(". -_") + +# These are single-character replacements, others are handled differently. +_FLIP_REPLACEMENTS = { + "l": "r", + "L": "R", + "r": "l", + "R": "L", +} + + +def flip_side_name(to_flip: str) -> str: + """Flip left and right indicators in the name. + + Basically a Python implementation of BLI_string_flip_side_name. + + >>> flip_side_name('bone_L.004') + 'bone_R.004' + >>> flip_side_name('left_bone') + 'right_bone' + >>> flip_side_name('Left_bone') + 'Right_bone' + >>> flip_side_name('LEFT_bone') + 'RIGHT_bone' + >>> flip_side_name('some.bone-RIGHT.004') + 'some.bone-LEFT.004' + >>> flip_side_name('some.bone-right.004') + 'some.bone-left.004' + >>> flip_side_name('some.bone-Right.004') + 'some.bone-Left.004' + >>> flip_side_name('some.bone-LEFT.004') + 'some.bone-RIGHT.004' + >>> flip_side_name('some.bone-left.004') + 'some.bone-right.004' + >>> flip_side_name('some.bone-Left.004') + 'some.bone-Right.004' + >>> flip_side_name('.004') + '.004' + >>> flip_side_name('L.004') + 'R.004' + """ + import string + + if len(to_flip) < 3: + # we don't flip names like .R or .L + return to_flip + + # We first check the case with a .### extension, let's find the last period. + number = "" + replace = to_flip + if to_flip[-1] in string.digits: + try: + index = to_flip.rindex(".") + except ValueError: + pass + else: + if to_flip[index + 1] in string.digits: + # TODO(Sybren): this doesn't handle "bone.1abc2" correctly. + number = to_flip[index:] + replace = to_flip[:index] + + if not replace: + # Nothing left after the number, so no flips necessary. + return replace + number + + if len(replace) == 1: + replace = _FLIP_REPLACEMENTS.get(replace, replace) + return replace + number + + # First case; separator . - _ with extensions r R l L. + if replace[-2] in _FLIP_SEPARATORS and replace[-1] in _FLIP_REPLACEMENTS: + replace = replace[:-1] + _FLIP_REPLACEMENTS[replace[-1]] + return replace + number + + # Second case; beginning with r R l L, with separator after it. + if replace[1] in _FLIP_SEPARATORS and replace[0] in _FLIP_REPLACEMENTS: + replace = _FLIP_REPLACEMENTS[replace[0]] + replace[1:] + return replace + number + + lower = replace.lower() + prefix = suffix = "" + if lower.startswith("right"): + bit = replace[0:2] + if bit == "Ri": + prefix = "Left" + elif bit == "RI": + prefix = "LEFT" + else: + prefix = "left" + replace = replace[5:] + elif lower.startswith("left"): + bit = replace[0:2] + if bit == "Le": + prefix = "Right" + elif bit == "LE": + prefix = "RIGHT" + else: + prefix = "right" + replace = replace[4:] + elif lower.endswith("right"): + bit = replace[-5:-3] + if bit == "Ri": + suffix = "Left" + elif bit == "RI": + suffix = "LEFT" + else: + suffix = "left" + replace = replace[:-5] + elif lower.endswith("left"): + bit = replace[-4:-2] + if bit == "Le": + suffix = "Right" + elif bit == "LE": + suffix = "RIGHT" + else: + suffix = "right" + replace = replace[:-4] + + return prefix + replace + suffix + number + + +if __name__ == '__main__': + import doctest + + print(f"Test result: {doctest.testmod()}") |