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 fcurve(action, path, index=-1, group=""): """Create a new fcurve in an action if it doesn't exist""" if group is None: group = "" fc = Get.fcurve(action, path, index) if fc is None: fc = action.fcurves.new(path, index=index, action_group=group) return fc
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)
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]