3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Fカーブのグラフの曲線をコピペする

Last updated at Posted at 2023-02-18

Blenderのアニメーションのカーブの曲線をコピペしたいという話があったので
アドオンとして作ってみました
23021901.gif
キーフレーム自体の値を変更せずに選択したキーフレームと次のキーフレームの間の補完の曲線が同じになるようにします.

Blenderのグラフエディタの機能の都合で使い勝手は今一つかもしれませんが目的の動作は可能かと思います
因みに 曲線の形はテキストデータとしてクリップボードに書き出すようにしたので
別途テキストとして保存しておいて 再度その文字列をクリップボードにコピーすることで別のファイル等でもカーブを再現できます

y_CopyPasetCurveEasing.py
bl_info = {
    "name" : "Copy Paset F-Curve Easing ",
    "author" : "Yukimituki",
    "description" : "",
    "blender" : (2, 80, 0),
    "version" : (0, 0, 1),
    "location" : "",
    "warning" : "",
    "category" : "F-Curves"
}

import bpy
import json
class CURVE_PT_EasingCopyPaste(bpy.types.Panel):
    bl_space_type = 'GRAPH_EDITOR'
    bl_region_type = 'UI'
    bl_category = "F-Curve"
    bl_label = "Easing copy&paste"
    def draw(self, context):
        layout = self.layout
        row = layout.row()
        row.label(text="Fカーブの補完曲線をコピペ")
        row = layout.row()
        row.label(text="コントロールポイントを一つだけ選択してください")
        row = layout.row(align=True)
        row.operator("cureasing.copy")
        row.operator("cureasing.paste")

class CURVE_OT_EasingCopy(bpy.types.Operator):
    bl_idname = "cureasing.copy"
    bl_label = "Copy"
    def execute(self, context):
        obj = bpy.context.active_object
        animation_data = obj.animation_data
        if  hasattr(animation_data,"action"):
            fcurves = animation_data.action.fcurves
            for curve in fcurves:
                if curve.select:
                    keyframe_count = len(curve.keyframe_points)
                    for i in range(keyframe_count -1):
                        if curve.keyframe_points[i].select_control_point:
                            dic = self.get_ease_settings(curve, i)
                            # テキストをクリップボードに
                            bpy.context.window_manager.clipboard = json.dumps(dic)
                        break
        return {'FINISHED'}
    def get_ease_settings(self, curve, i):
        attr_dic = {}
        point_1 = curve.keyframe_points[i]
        handle_1 = curve.keyframe_points[i].handle_right - point_1.co
        point_2 = curve.keyframe_points[i +1]
        handle_2 = curve.keyframe_points[i +1].handle_left - point_2.co
        diff = point_2.co - point_1.co
        if diff[1] == 0: return()
        attr_dic['interpolation'] = point_1.interpolation #'BEZIER', 'BACK', 'ELASTIC', ...
        attr_dic['back'] = point_1.back #interpolation 'BACK'
        attr_dic['easing'] = point_1.easing
        attr_dic['out_handle'] = [handle_1[0]/diff[0], handle_1[1]/diff[1]]
        attr_dic['out_type'] = point_1.handle_right_type
        attr_dic['in_handle'] = [handle_2[0]/diff[0], handle_2[1]/diff[1]]
        attr_dic['in_type'] = point_2.handle_left_type
        attr_dic['interpolation'] = point_1.interpolation
        attr_dic['amplitude'] = point_1.amplitude #interpolation 'ELASTIC'
        attr_dic['period'] = point_1.period/diff[0] #interpolation 'ELASTIC'
        return attr_dic

class CURVE_OT_EasingPaste(bpy.types.Operator):
    bl_idname = "cureasing.paste"
    bl_label = "Paste"
    def execute(self, context):
        # クリップボードのテキストを利用して生成
        dic = json.loads(bpy.context.window_manager.clipboard)
        obj = bpy.context.active_object
        animation_data = obj.animation_data
        if  hasattr(animation_data,"action"):
            fcurves = animation_data.action.fcurves
            for curve in fcurves:
                if curve.select:
                    keyframe_count = len(curve.keyframe_points)
                    for i in range(keyframe_count -1):
                        if curve.keyframe_points[i].select_control_point:
                            self.set_ease_settings(curve, i, dic)
                        break
        return {'FINISHED'}
    def set_ease_settings(self, curve, i, attr_dic):
        point_1 = curve.keyframe_points[i]
        point_2 = curve.keyframe_points[i +1]
        diff = point_2.co - point_1.co
        if diff[1] == 0: return()
        out_handle = attr_dic['out_handle']
        handle_1 = [ float(out_handle[0]) *diff[0] +point_1.co[0], float(out_handle[1]) *diff[1] +point_1.co[1] ]
        curve.keyframe_points[i].handle_right = handle_1
        point_1.handle_right_type = attr_dic['out_type']

        in_handle = attr_dic['in_handle']
        handle_2 = [ float(in_handle[0]) *diff[0] +point_2.co[0], float(in_handle[1]) *diff[1] +point_2.co[1] ]
        curve.keyframe_points[i +1].handle_left = handle_2
        point_2.handle_left_type = attr_dic['in_type']
        
        point_1.interpolation = attr_dic['interpolation']
        point_1.back = float(attr_dic['back'])
        point_1.easing = attr_dic['easing']
        point_1.interpolation = attr_dic['interpolation']
        point_1.amplitude = float(attr_dic['amplitude'])
        point_1.period = float(attr_dic['period']) *diff[0]

classes = (
    CURVE_PT_EasingCopyPaste,
    CURVE_OT_EasingCopy,
    CURVE_OT_EasingPaste,
    )

    
#####################################
def register():
    Scene = bpy.types.Scene
    for cls in classes:
        bpy.utils.register_class(cls)


def unregister():
    Scene = bpy.types.Scene
    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)


if __name__ == "__main__":
    register()
#######################################

冒頭の動画のカーブではこんなテキストがクリップボードにコピーされます

{"interpolation": "BEZIER", "back": 1.7015800476074219, "easing": "EASE_IN", "out_handle": [0.49889544436806127, 0.5134480499751198], "out_type": "ALIGNED", "in_handle": [-0.14829334459806742, -0.7493565886372822], "in_type": "FREE", "amplitude": 0.800000011920929, "period": 0.21578946866487203}

Blenderのカーブのベジェ曲線ではハンドルの座標はキーフレームと同様の座標値になるように
一旦 正規化して記録しています

3
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?