FCurveHandleCopy/__init__.py
MMaker 706525fde1
fix: Loop through fcurves and get selected only
This is potentially more expensive, but is more convenient
to the user when they may potentially have more than
one fcurve selected in the graph editor.
2022-03-26 03:17:18 -04:00

166 lines
5.3 KiB
Python

# Copyright (C) 2021 MMaker <mmaker@mmaker.moe>
#
# This file is part of FCurveHandleCopy.
#
# FCurveHandleCopy 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 3 of the License, or
# (at your option) any later version.
#
# FCurveHandleCopy 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 FCurveHandleCopy. If not, see <http://www.gnu.org/licenses/>.
bl_info = {
"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 = []
def inverse_lerp(minimum, maximum, val):
return (val - minimum) / (maximum - minimum)
def convert_handles_to_bezier(keyframes):
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):
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"
def execute(self, context):
if (context.selected_visible_fcurves):
fcurves = context.selected_visible_fcurves
G.selected_keys = []
for fcurve in fcurves:
for key in fcurve.keyframe_points:
if key.select_control_point:
G.selected_keys.append(key)
if (len(G.selected_keys) > 2):
self.report({"WARNING"}, "Please select exactly two keyframes when copying an ease.")
return {'CANCELLED'}
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'}
class FCurveHandlePasteValue(bpy.types.Operator):
"""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
selected_keys = {}
for fcurve in fcurves:
keys = fcurve.keyframe_points
for i in range(0, len(keys)):
if (keys[i].select_control_point):
if fcurve not in selected_keys:
selected_keys[fcurve] = []
selected_keys[fcurve].append(keys[i])
for fcurve, keys in selected_keys.items():
if (len(keys) == 0):
self.report({"WARNING"}, "Please select some keyframes to paste an ease to.")
return {'CANCELLED'}
if (len(keys) == 1):
# TODO: Implement logic for this soon
pass
else:
keys.pop() # TODO: Related to above, implement soon
for i, _ in enumerate(keys):
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]
return {'FINISHED'}
def menu_func(self, context):
self.layout.operator(FCurveHandleCopyValue.bl_idname)
self.layout.operator(FCurveHandlePasteValue.bl_idname)
classes = [
FCurveHandleCopyValue,
FCurveHandlePasteValue
]
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.GRAPH_HT_header.append(menu_func)
def unregister():
for cls in classes:
bpy.utils.unregister_class(cls)
bpy.types.GRAPH_HT_header.remove(menu_func)
if __name__ == "__main__":
try:
unregister()
except:
import sys
print("Error:", sys.exc_info()[0])
pass
register()