diff --git a/__init__.py b/__init__.py index 503876c..86700da 100644 --- a/__init__.py +++ b/__init__.py @@ -16,148 +16,143 @@ # along with FCurveHandleCopy. If not, see . bl_info = { - "name" : "FCurveHandleCopy", - "author" : "MMaker", - "description" : ":)", - "blender" : (2, 83, 0), - "version" : (0, 0, 1), - "category" : "Animation" + "name" : "FCurveHandleCopy", + "author" : "MMaker", + "description" : ":)", + "blender" : (2, 83, 0), + "version" : (0, 0, 1), + "category" : "Animation" } import bpy import mathutils +import bl_math class G: - selected_keys = [] - bezier = [] + selected_keys = [] + bezier = [] +def inverse_lerp(minimum, maximum, val): + return (val - minimum) / (maximum - minimum) + def convert_handles_to_bezier(keyframes): - bezier = [] + handles = [keyframes[0].handle_right, keyframes[1].handle_left] - in_key = keyframes[0] - out_key = keyframes[1] - - in_handle = in_key.handle_right - out_handle = out_key.handle_left - - bezier.append(in_handle[0] / (abs(in_key.co[0] - out_key.co[0]))) # x1 - bezier.append(in_handle[1] / (abs(in_key.co[1] - out_key.co[1]))) # y1 - bezier.append(out_handle[0] / (abs(in_key.co[0] - out_key.co[0]))) # x2 - bezier.append(out_handle[1] / (abs(in_key.co[1] - out_key.co[1]))) # y2 - - return bezier + 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): - x_diff = abs(in_key.co[0] - out_key.co[0]) - y_diff = abs(in_key.co[1] - out_key.co[1]) - - y_direction = (1 if (in_key.co[1] - out_key.co[1]) < 0 else -1) - - new_in_handle = mathutils.Vector(( - in_key.co[0] + (G.bezier[0] * x_diff), - in_key.co[1] + (G.bezier[1] * y_diff * y_direction) - )) - - new_out_handle = mathutils.Vector(( - in_key.co[0] + (G.bezier[2] * x_diff), - in_key.co[1] + (G.bezier[3] * y_diff * y_direction) - )) - - return [new_in_handle, new_out_handle] + handles = list(map( + lambda fac: list(map( + lambda dimension: bl_math.lerp(in_key.co[dimension], out_key.co[dimension], fac[dimension]), + range(2) + )), + G.bezier + )) + + return [ + mathutils.Vector( + (handles[0][0], handles[0][1]) + ), + mathutils.Vector( + (handles[1][0], handles[1][1]) + ) + ] class FCurveHandleCopyValue(bpy.types.Operator): - """Copy FCurve handle values""" - bl_idname = "anim.mmaker_fcurve_handle_copy_values" - bl_label = "Copy" + """Copy FCurve handle values""" + bl_idname = "anim.mmaker_fcurve_handle_copy_values" + bl_label = "Copy" - def execute(self, context): - if (context.selected_visible_fcurves): - fcurves = context.selected_visible_fcurves - G.selected_keys = [] + def execute(self, context): + if (context.selected_visible_fcurves): + fcurves = context.selected_visible_fcurves + G.selected_keys = [] - if (len(fcurves) > 1): - self.report({"WARNING"}, "Please only select one curve when copying an ease.") - return {'CANCELLED'} - - for keyframe in fcurves[0].keyframe_points: - if (len(G.selected_keys) > 2): - self.report({"WARNING"}, "Please select exactly two keyframes when copying an ease.") - return {'CANCELLED'} - - if (keyframe.select_control_point): - G.selected_keys.append(keyframe) - - 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) + if (len(fcurves) > 1): + self.report({"WARNING"}, "Please only select one curve when copying an ease.") + return {'CANCELLED'} + + G.selected_keys = list(filter(lambda x: x.select_control_point, fcurves[0].keyframe_points)) + + 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'} + return {'FINISHED'} class FCurveHandlePasteValue(bpy.types.Operator): - """Paste FCurve handle values""" - bl_idname = "anim.mmaker_fcurve_handle_paste_values" - bl_label = "Paste" + """Paste FCurve handle values""" + bl_idname = "anim.mmaker_fcurve_handle_paste_values" + bl_label = "Paste" - def execute(self, context): - if (context.selected_visible_fcurves): - fcurves = context.selected_visible_fcurves + def execute(self, context): + if (context.selected_visible_fcurves): + fcurves = context.selected_visible_fcurves - for fcurve in fcurves: - 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 in fcurves: + keys = fcurve.keyframe_points + selected_keys = [] + for i in range(0, len(keys)): + if (keys[i].select_control_point): + selected_keys.append(i) - if (len(selected_keys) == 0): - self.report({"WARNING"}, "Please select some keyframes to paste an ease to.") - return {'CANCELLED'} - if (len(selected_keys) == 1): - # TODO: Implement logic for this soon - pass - else: - 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] + if (len(selected_keys) == 0): + self.report({"WARNING"}, "Please select some keyframes to paste an ease to.") + return {'CANCELLED'} + if (len(selected_keys) == 1): + # TODO: Implement logic for this soon + pass + else: + 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'} + return {'FINISHED'} def menu_func(self, context): - self.layout.operator(FCurveHandleCopyValue.bl_idname) - self.layout.operator(FCurveHandlePasteValue.bl_idname) + self.layout.operator(FCurveHandleCopyValue.bl_idname) + self.layout.operator(FCurveHandlePasteValue.bl_idname) classes = [ - FCurveHandleCopyValue, - FCurveHandlePasteValue + FCurveHandleCopyValue, + FCurveHandlePasteValue ] def register(): - for cls in classes: - bpy.utils.register_class(cls) + for cls in classes: + bpy.utils.register_class(cls) - bpy.types.GRAPH_HT_header.append(menu_func) + bpy.types.GRAPH_HT_header.append(menu_func) def unregister(): - for cls in classes: - bpy.utils.unregister_class(cls) + for cls in classes: + bpy.utils.unregister_class(cls) - bpy.types.GRAPH_HT_header.remove(menu_func) + bpy.types.GRAPH_HT_header.remove(menu_func) if __name__ == "__main__": - try: - unregister() - except: - import sys - print("Error:", sys.exc_info()[0]) - pass - register() \ No newline at end of file + try: + unregister() + except: + import sys + print("Error:", sys.exc_info()[0]) + pass + register()