def get_obj_from_path(path, obj=None): if path.startswith('bpy.data.'): obj2 = eval(path.split('"]', 1)[0] + '"]') if Is.object(obj2): obj = obj2 return obj
def keyingset(context, default=True, **kargs): """ Use bpy.ops to insert keyframe. default means to only use the keying set if the keying set button is enabled """ insert_key = kargs.get('insert_key') if not keyframe.poll_insert(context, insert_key): return ks = context.scene.keying_sets_all.active if (default and not keyframe.use_keyingset(context)) or \ ks is None: ks = 'LocRotScale' else: ks = ks.bl_idname bones = kargs.get('bones', list()) objects = kargs.get('objects', list()) selected = kargs.get('selected', list()) # if bones is None: bones = Get.selected_pose_bones(context) # if objects is None: objects = Get.selected_objects(context) for src in selected: if Is.posebone(src): bones.append(src) elif Is.object(src): objects.append(src) else: assert None, ("This is not a bone or object", src) if kargs.get('skip_bones', False): # This step removes bones from badBonePrefixes for src in bones.copy(): if not keyframe.poll_insert(context, insert_key, src=src): bones.remove(src) # if keyframe.use_keyingset(): # bpy.ops.anim.keyframe_insert(type=ks, confirm_success=False) # # else: # for src in KSI.iter(): # keyframe.all(src) if (bones or objects): try: return bpy.ops.anim.keyframe_insert(dict(selected_pose_bones=bones, selected_objects=objects), type=ks, confirm_success=False) except Exception as ex: pass else: "Nothing to keyframe"
def icon_from_type(src): "return an Icon id for the specified item's type" if Is.object(src): if src.type == 'LIGHT_PROBE': icon = 'LIGHTPROBE_' + src.data.type else: icon = src.type + '_DATA' elif Is.bone(src) or Is.editbone(src) or Is.posebone(src): icon = 'BONE_DATA' else: icon = 'ERROR' utils.debug("Can't find icon type for ", src, type(src)) return icon
def select(target, value=True): """Select or Deselect an item""" if Is.object(target): target.select_set(value) elif Is.posebone(target): target.bone.select = value elif Is.bone(target) or Is.editbone(target): target.select = value elif target is None: pass else: # Give error assert None, ( "Error: zpy\\Set.select() can't use the provided target \n", target, )
def active(context, target): """Set target as active scene object or bone""" objects = Get.objects(context) # Remember previous active previous = Get.active(context) selected = Is.selected(target) # Set the active if Is.object(target): obj = target elif Is.posebone(target): obj = target.id_data obj.data.bones.active = obj.data.bones.get(target.name) elif isinstance(target, bpy.types.Armature): obj = Get.rig(context, target) elif Is.bone(target): obj = Get.rig(context, target) bones = target.id_data.bones bones.active = bones.get(target.name) elif Is.editbone(target): obj = Get.rig(context, target) if obj: in_edit = (obj.mode == 'EDIT') else: in_edit = (context.mode == 'EDIT_ARMATURE') if in_edit: bones = target.id_data.edit_bones bones.active = bones.get(target.name) elif target is None: obj = None # debug("Set.active() has None as the target") else: assert None, ("Set.active() can't use the provided target", target) if (target and Is.selected(target) != selected): # When setting a bone as active in a rig, it gets selected as well. Set.select(target, selected) objects.active = obj return previous
def action_fcurves(src, actions=None, raw_action=True): "Find actions used by a bone/object and return it's action + fcurves" fcurves = list() if actions is None: rig = src.id_data strips = Get.strips(rig, selected=False) anim = rig.animation_data an_act = getattr(anim, 'action', None) if raw_action else None actions = {an_act, *(s.action for s in strips)} for action in actions: if not action: continue for fc in action.fcurves: if any(( Is.object(src), Is.posebone(src) and fc.data_path.find(f'bones[\"{src.name}\"]') != -1, )): fcurves.append((action, fc)) return fcurves
def matrix(src, matrix, local=False, basis=False): """ Set the visual transforms for a bone or object The parameters vary, so the input matrix should too: editbone. matrix bone. matrix, matrix_local posebone. matrix, matrix_basis, matrix_channel object. matrix_world, matrix_basis, matrix_local, matrix_parent_inverse """ if Is.object(src): if basis: src.matrix_basis = matrix elif local: src.matrix_local = matrix else: src.matrix_world = matrix else: if basis or local: if Is.posebone(src): src.matrix_basis = matrix elif Is.bone(src): src.matrix_local = matrix else: src.matrix = matrix else: src.matrix = matrix
def driver(src, path, **kargs): driver_type = kargs.get('driver_type', None) # 'AVERAGE', 'Sum Values', 'SCRIPTED', 'Minimum Value', 'Maximum Value expression = kargs.get('expression', None) frames = kargs.get('frames', list()) # keyframe.co for the driver's fcurve name = kargs.get('name', "var") # Name of the variable added to the driver overwrite = kargs.get('overwrite', False) # Delete the existing driver rotation_mode = kargs.get('rotation_mode', 'AUTO') target = kargs.get('target', None) target_path = kargs.get('target_path', '') transform_space = kargs.get('transform_space', 'LOCAL_SPACE') transform_type = kargs.get('transform_type', 'LOC_X') var_type = kargs.get('var_type', None) # 'SINGLE_PROP', 'TRANSFORMS', 'Rotational Difference', 'Distance' if var_type is None: if target and (not target_path): var_type = 'TRANSFORMS' else: var_type = 'SINGLE_PROP' Driver = Get.driver(src, path) if not Driver: Driver = src.driver_add(path) overwrite = True if overwrite: while Driver.keyframe_points: Driver.keyframe_points.remove(Driver.keyframe_points[0]) if frames: if overwrite: Driver.extrapolation = 'LINEAR' while Driver.modifiers: Driver.modifiers.remove(Driver.modifiers[0]) Driver.keyframe_points.add(len(frames)) for key, co in zip(Driver.keyframe_points[:], frames): key.interpolation = 'LINEAR' key.co = co driver = Driver.driver if overwrite: if (expression is None): if (driver_type is None): driver_type = 'AVERAGE' elif (driver.type == 'SCRIPTED'): driver.expression = name while driver.variables: driver.variables.remove(driver.variables[0]) if expression is not None: driver.expression = expression if driver_type: driver.type = driver_type var = driver.variables.new() var.name = name var.type = var_type var_target = var.targets[0] if target: is_pose = Is.posebone(target) is_bone = Is.bone(target) or Is.editbone(target) is_obj = Is.object(target) if is_obj: var_target.id = target elif (is_pose or is_bone): var_target.id = target.id_data var_target.bone_target = target.name if target_path and (not target_path.startswith( ('pose.bones', 'bones'))): if is_pose: text = f'pose.bones["{target.name}"]' else: text = f'bones["{target.name}"]' if (target_path[0] != '['): text += '.' target_path = text + target_path else: try: var_target.id = target except: var_target.id = target.id_data var_target.data_path = target_path var_target.rotation_mode = rotation_mode var_target.transform_space = transform_space var_target.transform_type = transform_type return Driver
def visible(context, object, value=True, **kargs): """ Set an object's (or bone's object's) visibility to the specified value """ scn = context.scene if not Is.object(object): if isinstance(object, bpy.types.Collection): found = False def loop(root, tree=list()): nonlocal found if root.collection == object: return True for child in root.children: if loop(child, tree): found = True tree.append(child) break if found: return tree view_layer = kargs.get('view_layer', False) if not view_layer: object.hide_viewport = not value if value or view_layer: # Only enables the collection for the view layer once tree = loop(context.view_layer.layer_collection) for col in tree: if (col.exclude == value) and (col.name == object.name): # When a collection is enabled in the view layer, # all of its child collections are as well. col.exclude = not value if value and col.collection.hide_viewport: col.collection.hide_viewport = False elif Is.posebone(object): return Set.visible(context, object.id_data, value) elif Is.bone(object) or Is.editbone(object): return Set.visible(context, Get.rig(context, object), value) else: assert None, ( "Set.visible() does not work with the specified item", object, ) return Set.in_scene(context, object) is_visible = Is.visible(context, object) object_visible = not object.hide_viewport # if Is.visible(context, object) is value: # return visible while (Is.visible(context, object) is not value): "If object isn't in the desired visiblity, loop until it is" if (object.hide_viewport is value): object.hide_viewport = not value continue is_visible = object_visible view = None for collection in object.users_collection: view = context.view_layer.layer_collection.children.get( collection.name) if not view: # collection isn't in scene or whatever continue if view.hide_viewport is value: view.hide_viewport = not value break if view is None: assert None, ( "Set.visible(): Object[", object, "] \nis hidden from viewport and I don't know how to change it" ) # collection.hide_viewport = value break return is_visible
def group_name(src): if Is.object(src): return "Object Transforms" else: return src.name
def manual(context, src, path, **kargs): "Insert a keyframe manually. Sub is for sub targets, like constraints" # kargs: # sub=None, index=-1, frame=None, value=None, # group=None, insert_key=None, action=None, context=None insert_key = kargs.get('insert_key', None) sub = kargs.get('sub', None) index = kargs.get('index', -1) frame = kargs.get('frame', None) value = kargs.get('value', None) group = kargs.get('group', None) action = kargs.get('action', None) results = kargs.get('results', list()) options = kargs.get('options', set()) delete = kargs.get('delete', False) keyframe_type = kargs.get('type', context.scene.tool_settings.keyframe_type) if not keyframe.poll_insert(context, insert_key, src=src): return results # if frame is None: # frame = context.scene.frame_current # if group is None: # group = keyframe.group_name(src) # src.keyframe_insert(path, index=index, frame=frame, group=group) # return if group is None: group = keyframe.group_name(src) obj = src.id_data anim = obj.animation_data_create() if action is None: key_in_action = True action = anim.action if action is None: action = anim.action = bpy.data.actions.new("Action") else: # When using a specified action, don't insert # keyframes later to determine the property's value key_in_action = False if frame is None: frame = context.scene.frame_current_final strip = Get.active_strip(anim) if anim.use_tweak_mode else None if strip: if not (strip.frame_start <= frame <= strip.frame_end): # frame outside of the strip's bounds key_in_action = False tweak_frame = Get.frame_to_strip(context, anim, frame=frame) else: tweak_frame = frame if Is.posebone(src) and not path.startswith('pose.bones'): keypath = utils.string('pose.bones[\"', src.name, '\"]', '' if path.startswith('[') else '.', path) elif not Is.object(src) and hasattr(src, 'path_from_id'): keypath = src.path_from_id(path) else: keypath = path # Find the value(s) to insert keyframes for if hasattr(sub, path): base = getattr(sub, path) elif hasattr(src, path): base = getattr(src, path) else: base = eval(f'{obj!r}.{keypath}') if value is None: prop = base else: if Is.iterable(base) and not Is.iterable(value): prop = [value for i in base] elif not Is.iterable(base) and Is.iterable(value): prop = value[(index, 0)[index == -1]] else: prop = value if (not Is.iterable(prop)): if index != -1: index = -1 if (not Is.iterable(prop)): props = [(index, prop)] else: props = [(index, prop[index])] elif (index == -1): props = list(enumerate(prop)) else: props = [(index, prop[index])] def save_quats(): quats = [0, 0, 0, 0] for index in range(4): fc = Get.fcurve(action, keypath, index=index) if fc: quats[index] = len(fc.keyframe_points) return quats def restore_quats(skip): nonlocal quats for index in range(4): if index == skip: continue fc = Get.fcurve(action, keypath, index=index) if fc and len(fc.keyframe_points) > quats[index]: for key in fc.keyframe_points: if key.co.x == tweak_frame: fc.keyframe_points.remove(key) break else: # Shouldn't happen but backup in case fail to find key key_args = dict(index=index, frame=frame, group=group) if sub is None: src.keyframe_delete(path, **key_args) else: sub.keyframe_delete(path, **key_args) if path.endswith('rotation_quaternion') and ( (strip and strip.blend_type == 'COMBINE') or (not strip and anim.action_blend_type == 'COMBINE')): # Combine forces keyframe insertion on all 4 channels, so reset them quats = save_quats() else: quats = None # Create curve(s) (if needed) and keyframe(s) for (i, v) in props: fc = Get.fcurve(action, keypath, i) new_fc = not bool(fc) if new_fc: fc = New.fcurve(action, keypath, index=i, group=group) if fc.lock: # Internal ops don't allow keyframing locked channels, so :p results.append((fc, None)) continue if delete: results.append((fc, src.keyframe_delete(path, frame=frame))) continue count = len(fc.keyframe_points) if (value is None) and key_in_action: key_args = dict(index=i, frame=frame, group=group, options=options) if sub is None: src.keyframe_insert(path, **key_args) else: sub.keyframe_insert(path, **key_args) v = fc.evaluate(tweak_frame) key = fc.keyframe_points.insert(tweak_frame, v, options={'FAST'}) # options=set({'REPLACE', 'NEEDED', 'FAST'}) # src.keyframe_insert(path, index=i, frame=frame, group=group) if quats: restore_quats(skip=i) quats[i] = len(fc.keyframe_points) # Update key count for current index, to not remove it later # Update keyframe to use default preferences values edit = utils.prefs().edit key.handle_left_type = key.handle_right_type = \ edit.keyframe_new_handle_type if new_fc: # When inserting keyframes, only change their interpolation type if the fcurve is new key.interpolation = edit.keyframe_new_interpolation_type if len(fc.keyframe_points) > count: # New keyframe was added key.type = keyframe_type results.append((fc, key)) if kargs.get('index', -1) == -1: return results else: return results[0]