2021-06-12 10:31:52 -04:00
# 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 = {
2021-07-09 21:57:31 -04:00
" name " : " FCurveHandleCopy " ,
" author " : " MMaker " ,
" description " : " :) " ,
" blender " : ( 2 , 83 , 0 ) ,
" version " : ( 0 , 0 , 1 ) ,
" category " : " Animation "
2021-06-12 10:31:52 -04:00
}
import bpy
import mathutils
2021-07-09 21:57:31 -04:00
import bl_math
2021-06-12 10:31:52 -04:00
class G :
2021-07-09 21:57:31 -04:00
selected_keys = [ ]
bezier = [ ]
2021-06-12 10:31:52 -04:00
2021-07-09 21:57:31 -04:00
def inverse_lerp ( minimum , maximum , val ) :
return ( val - minimum ) / ( maximum - minimum )
2021-06-12 10:31:52 -04:00
def convert_handles_to_bezier ( keyframes ) :
2021-07-09 21:57:31 -04:00
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
2021-06-12 10:31:52 -04:00
def generate_new_handles ( in_key , out_key ) :
2021-07-09 21:57:31 -04:00
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 ] )
)
]
2021-06-12 10:31:52 -04:00
class FCurveHandleCopyValue ( bpy . types . Operator ) :
2021-07-09 21:57:31 -04:00
""" 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 = [ ]
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 ' }
2021-06-12 10:31:52 -04:00
class FCurveHandlePasteValue ( bpy . types . Operator ) :
2021-07-09 21:57:31 -04:00
""" 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
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 ]
return { ' FINISHED ' }
2021-06-12 10:31:52 -04:00
def menu_func ( self , context ) :
2021-07-09 21:57:31 -04:00
self . layout . operator ( FCurveHandleCopyValue . bl_idname )
self . layout . operator ( FCurveHandlePasteValue . bl_idname )
2021-06-12 10:31:52 -04:00
classes = [
2021-07-09 21:57:31 -04:00
FCurveHandleCopyValue ,
FCurveHandlePasteValue
2021-06-12 10:31:52 -04:00
]
def register ( ) :
2021-07-09 21:57:31 -04:00
for cls in classes :
bpy . utils . register_class ( cls )
2021-06-12 10:31:52 -04:00
2021-07-09 21:57:31 -04:00
bpy . types . GRAPH_HT_header . append ( menu_func )
2021-06-12 10:31:52 -04:00
def unregister ( ) :
2021-07-09 21:57:31 -04:00
for cls in classes :
bpy . utils . unregister_class ( cls )
2021-06-12 10:31:52 -04:00
2021-07-09 21:57:31 -04:00
bpy . types . GRAPH_HT_header . remove ( menu_func )
2021-06-12 10:31:52 -04:00
if __name__ == " __main__ " :
2021-07-09 21:57:31 -04:00
try :
unregister ( )
except :
import sys
print ( " Error: " , sys . exc_info ( ) [ 0 ] )
pass
register ( )