Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
3b5ba4f977 | |||
5391ebaa42 | |||
2e94e2c684 | |||
3d5e65bb88 | |||
0d82095ea5 | |||
5a520c59c9 | |||
46ccc77235 | |||
706525fde1 |
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
.vscode
|
.vscode
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
__pycache__
|
__pycache__
|
||||||
|
*.zip
|
130
__init__.py
130
__init__.py
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (C) 2021 MMaker <mmaker@mmaker.moe>
|
# Copyright (C) 2021-2022 MMaker <mmaker@mmaker.moe>
|
||||||
#
|
#
|
||||||
# This file is part of FCurveHandleCopy.
|
# This file is part of FCurveHandleCopy.
|
||||||
#
|
#
|
||||||
|
@ -20,13 +20,13 @@ bl_info = {
|
||||||
"author" : "MMaker",
|
"author" : "MMaker",
|
||||||
"description" : ":)",
|
"description" : ":)",
|
||||||
"blender" : (2, 83, 0),
|
"blender" : (2, 83, 0),
|
||||||
"version" : (0, 0, 1),
|
"version" : (0, 0, 2),
|
||||||
"category" : "Animation"
|
"category" : "Animation"
|
||||||
}
|
}
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import mathutils
|
import mathutils
|
||||||
import bl_math
|
import bl_math # type: ignore
|
||||||
|
|
||||||
class G:
|
class G:
|
||||||
selected_keys = []
|
selected_keys = []
|
||||||
|
@ -35,17 +35,67 @@ class G:
|
||||||
def inverse_lerp(minimum, maximum, val):
|
def inverse_lerp(minimum, maximum, val):
|
||||||
return (val - minimum) / (maximum - minimum)
|
return (val - minimum) / (maximum - minimum)
|
||||||
|
|
||||||
def convert_handles_to_bezier(keyframes):
|
def create_bezier(handles, co_left_side, co_right_side):
|
||||||
handles = [keyframes[0].handle_right, keyframes[1].handle_left]
|
return list(
|
||||||
|
map(
|
||||||
bezier = list(map(
|
|
||||||
lambda x: list(map(
|
lambda x: list(map(
|
||||||
lambda v, dimension: inverse_lerp(keyframes[0].co[dimension], keyframes[1].co[dimension], v),
|
lambda v, dimension: inverse_lerp(co_left_side[dimension], co_right_side[dimension], v),
|
||||||
x,
|
x,
|
||||||
range(2)
|
range(2)
|
||||||
)),
|
)),
|
||||||
handles
|
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)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
return bezier
|
return bezier
|
||||||
|
|
||||||
def generate_new_handles(in_key, out_key):
|
def generate_new_handles(in_key, out_key):
|
||||||
|
@ -74,19 +124,20 @@ class FCurveHandleCopyValue(bpy.types.Operator):
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
if (context.selected_visible_fcurves):
|
if (context.selected_visible_fcurves):
|
||||||
fcurves = context.selected_visible_fcurves
|
fcurves = context.selected_visible_fcurves
|
||||||
G.selected_keys = []
|
G.selected_keys = {}
|
||||||
|
|
||||||
if (len(fcurves) > 1):
|
for fcurve in fcurves:
|
||||||
self.report({"WARNING"}, "Please only select one curve when copying an ease.")
|
for key_index, key in enumerate(fcurve.keyframe_points):
|
||||||
return {'CANCELLED'}
|
if key.select_control_point:
|
||||||
|
if fcurve not in G.selected_keys:
|
||||||
G.selected_keys = list(filter(lambda x: x.select_control_point, fcurves[0].keyframe_points))
|
G.selected_keys[fcurve] = []
|
||||||
|
G.selected_keys[fcurve].append(key_index)
|
||||||
if (len(G.selected_keys) != 2):
|
|
||||||
self.report({"WARNING"}, "Please select exactly two keyframes when copying an ease.")
|
|
||||||
return {'CANCELLED'}
|
|
||||||
|
|
||||||
|
if G.selected_keys:
|
||||||
G.bezier = convert_handles_to_bezier(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.")
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
@ -99,30 +150,35 @@ class FCurveHandlePasteValue(bpy.types.Operator):
|
||||||
if (context.selected_visible_fcurves):
|
if (context.selected_visible_fcurves):
|
||||||
fcurves = context.selected_visible_fcurves
|
fcurves = context.selected_visible_fcurves
|
||||||
|
|
||||||
for fcurve in fcurves:
|
selected_keys = {}
|
||||||
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):
|
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)
|
||||||
|
|
||||||
|
for fcurve, key_indexes in selected_keys.items():
|
||||||
|
if (len(key_indexes) == 0):
|
||||||
self.report({"WARNING"}, "Please select some keyframes to paste an ease to.")
|
self.report({"WARNING"}, "Please select some keyframes to paste an ease to.")
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
if (len(selected_keys) == 1):
|
if (len(key_indexes) == 1):
|
||||||
# TODO: Implement logic for this soon
|
# TODO: Implement logic for this soon
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
selected_keys.pop() # TODO: Related to above, implement soon
|
key_indexes.pop() # TODO: Related to above, implement soon
|
||||||
for i in selected_keys:
|
for i in key_indexes:
|
||||||
if (i < len(keys) - 1):
|
f_keys = fcurve.keyframe_points
|
||||||
new_handles = generate_new_handles(keys[i], keys[i + 1])
|
if (i < len(f_keys) - 1):
|
||||||
keys[i].interpolation = 'BEZIER'
|
new_handles = generate_new_handles(f_keys[i], f_keys[i + 1])
|
||||||
keys[i + 1].interpolation = 'BEZIER'
|
f_keys[i].interpolation = 'BEZIER'
|
||||||
keys[i].handle_right_type = 'FREE'
|
f_keys[i + 1].interpolation = 'BEZIER'
|
||||||
keys[i + 1].handle_left_type = 'FREE'
|
f_keys[i].handle_right_type = 'FREE'
|
||||||
keys[i].handle_right = new_handles[0]
|
f_keys[i + 1].handle_left_type = 'FREE'
|
||||||
keys[i + 1].handle_left = new_handles[1]
|
f_keys[i].handle_right = new_handles[0]
|
||||||
|
f_keys[i + 1].handle_left = new_handles[1]
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user