diff --git a/.gitignore b/.gitignore index fcf1046..6525d15 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ .vscode *.code-workspace -__pycache__ -*.zip \ No newline at end of file +__pycache__ \ No newline at end of file diff --git a/__init__.py b/__init__.py index 85fc2f7..86700da 100644 --- a/__init__.py +++ b/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2021-2022 MMaker +# Copyright (C) 2021 MMaker # # This file is part of FCurveHandleCopy. # @@ -20,13 +20,13 @@ bl_info = { "author" : "MMaker", "description" : ":)", "blender" : (2, 83, 0), - "version" : (0, 0, 2), + "version" : (0, 0, 1), "category" : "Animation" } import bpy import mathutils -import bl_math # type: ignore +import bl_math class G: selected_keys = [] @@ -34,68 +34,18 @@ class G: def inverse_lerp(minimum, maximum, val): return (val - minimum) / (maximum - minimum) - -def create_bezier(handles, co_left_side, co_right_side): - return list( - map( - lambda x: list(map( - lambda v, dimension: inverse_lerp(co_left_side[dimension], co_right_side[dimension], v), - x, - range(2) - )), - handles - ) - ) - + def convert_handles_to_bezier(keyframes): - # TODO(mmaker): Some of the logic here, particularly when selecting one key, - # is likely not the correct way to calculate the bezier. - # Should do a second pass over this and see if it could be cleaned up to be more accurate. - # (I'm not very math pilled, sorry) - # - # This at minimum should handle any types of user selections though. - # (Two keys, a single key, several keys across different fcurves) - - beziers = [] - for fcurve, key_indexes in keyframes.items(): - f_keys = fcurve.keyframe_points - - # Case when only one key is selected - # TODO(mmaker): Clean up this logic - if len(key_indexes) == 1: - key = f_keys[key_indexes[0]] - beziers.append(create_bezier([ - key.handle_left, key.handle_right - ], key.co, [0.0, 0.0])) - - for i in key_indexes[:-1]: - # NOTE(mmaker): This naming could probably be better, lol - handle_left_side = f_keys[i].handle_right - co_left_side = f_keys[i].co - if i >= len(f_keys): - # Case when selected key is the last in the fcurve - handle_right_side = [0.0, 0.0] - co_right_side = f_keys[i].co - else: - handle_right_side = f_keys[i + 1].handle_left - co_right_side = f_keys[i + 1].co - - handles = [handle_left_side, handle_right_side] - beziers.append(create_bezier(handles, co_left_side, co_right_side)) - - # Average beziers - # NOTE(mmaker): I'm sure there is a way to vectorize this, but until then, B) - bezier = [ - [ - sum([x[0] for x in list(zip(*beziers))[0]]) / len(beziers), - sum([x[1] for x in list(zip(*beziers))[0]]) / len(beziers) - ], - [ - sum([x[0] for x in list(zip(*beziers))[1]]) / len(beziers), - sum([x[1] for x in list(zip(*beziers))[1]]) / len(beziers) - ] - ] + handles = [keyframes[0].handle_right, keyframes[1].handle_left] + bezier = list(map( + lambda x: list(map( + lambda v, dimension: inverse_lerp(keyframes[0].co[dimension], keyframes[1].co[dimension], v), + x, + range(2) + )), + handles + )) return bezier def generate_new_handles(in_key, out_key): @@ -124,20 +74,19 @@ class FCurveHandleCopyValue(bpy.types.Operator): def execute(self, context): if (context.selected_visible_fcurves): fcurves = context.selected_visible_fcurves - G.selected_keys = {} + G.selected_keys = [] + + if (len(fcurves) > 1): + self.report({"WARNING"}, "Please only select one curve when copying an ease.") + return {'CANCELLED'} - for fcurve in fcurves: - for key_index, key in enumerate(fcurve.keyframe_points): - if key.select_control_point: - if fcurve not in G.selected_keys: - G.selected_keys[fcurve] = [] - G.selected_keys[fcurve].append(key_index) + G.selected_keys = list(filter(lambda x: x.select_control_point, fcurves[0].keyframe_points)) - if G.selected_keys: - G.bezier = convert_handles_to_bezier(G.selected_keys) - else: - self.report({"WARNING"}, "Please select some keyframes to copy an ease from.") + if (len(G.selected_keys) != 2): + self.report({"WARNING"}, "Please select exactly two keyframes when copying an ease.") return {'CANCELLED'} + + G.bezier = convert_handles_to_bezier(G.selected_keys) return {'FINISHED'} @@ -150,35 +99,30 @@ class FCurveHandlePasteValue(bpy.types.Operator): if (context.selected_visible_fcurves): fcurves = context.selected_visible_fcurves - selected_keys = {} - for fcurve in fcurves: - f_keys = fcurve.keyframe_points - for i in range(0, len(f_keys)): - if (f_keys[i].select_control_point): - if fcurve not in selected_keys: - selected_keys[fcurve] = [] - selected_keys[fcurve].append(i) + keys = fcurve.keyframe_points + selected_keys = [] + for i in range(0, len(keys)): + if (keys[i].select_control_point): + selected_keys.append(i) - for fcurve, key_indexes in selected_keys.items(): - if (len(key_indexes) == 0): + if (len(selected_keys) == 0): self.report({"WARNING"}, "Please select some keyframes to paste an ease to.") return {'CANCELLED'} - if (len(key_indexes) == 1): + if (len(selected_keys) == 1): # TODO: Implement logic for this soon pass else: - key_indexes.pop() # TODO: Related to above, implement soon - for i in key_indexes: - f_keys = fcurve.keyframe_points - if (i < len(f_keys) - 1): - new_handles = generate_new_handles(f_keys[i], f_keys[i + 1]) - f_keys[i].interpolation = 'BEZIER' - f_keys[i + 1].interpolation = 'BEZIER' - f_keys[i].handle_right_type = 'FREE' - f_keys[i + 1].handle_left_type = 'FREE' - f_keys[i].handle_right = new_handles[0] - f_keys[i + 1].handle_left = new_handles[1] + selected_keys.pop() # TODO: Related to above, implement soon + for i in selected_keys: + if (i < len(keys) - 1): + new_handles = generate_new_handles(keys[i], keys[i + 1]) + keys[i].interpolation = 'BEZIER' + keys[i + 1].interpolation = 'BEZIER' + keys[i].handle_right_type = 'FREE' + keys[i + 1].handle_left_type = 'FREE' + keys[i].handle_right = new_handles[0] + keys[i + 1].handle_left = new_handles[1] return {'FINISHED'}