diff options
-rw-r--r-- | release/scripts/startup/bl_ui/space_dopesheet.py | 2 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/space_graph.py | 1 | ||||
-rw-r--r-- | source/blender/blenlib/BLI_string.h | 3 | ||||
-rw-r--r-- | source/blender/blenlib/intern/string.c | 29 | ||||
-rw-r--r-- | source/blender/editors/animation/keyframes_general.c | 121 | ||||
-rw-r--r-- | source/blender/editors/include/ED_keyframes_edit.h | 2 | ||||
-rw-r--r-- | source/blender/editors/space_action/action_edit.c | 10 | ||||
-rw-r--r-- | source/blender/editors/space_action/action_ops.c | 4 | ||||
-rw-r--r-- | source/blender/editors/space_graph/graph_edit.c | 11 | ||||
-rw-r--r-- | source/blender/editors/space_graph/graph_ops.c | 4 |
10 files changed, 170 insertions, 17 deletions
diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py index 0458ffe3377..f901d9d9262 100644 --- a/release/scripts/startup/bl_ui/space_dopesheet.py +++ b/release/scripts/startup/bl_ui/space_dopesheet.py @@ -131,6 +131,8 @@ class DOPESHEET_HT_header(Header): row = layout.row(align=True) row.operator("action.copy", text="", icon='COPYDOWN') row.operator("action.paste", text="", icon='PASTEDOWN') + row.operator("action.paste", text="", icon='PASTEFLIPDOWN').flipped = True + class DOPESHEET_MT_editor_menus(Menu): diff --git a/release/scripts/startup/bl_ui/space_graph.py b/release/scripts/startup/bl_ui/space_graph.py index 5861bc0dc95..6b784033bfb 100644 --- a/release/scripts/startup/bl_ui/space_graph.py +++ b/release/scripts/startup/bl_ui/space_graph.py @@ -52,6 +52,7 @@ class GRAPH_HT_header(Header): row = layout.row(align=True) row.operator("graph.copy", text="", icon='COPYDOWN') row.operator("graph.paste", text="", icon='PASTEDOWN') + row.operator("graph.paste", text="", icon='PASTEFLIPDOWN').flipped = True row = layout.row(align=True) if st.has_ghost_curves: diff --git a/source/blender/blenlib/BLI_string.h b/source/blender/blenlib/BLI_string.h index d6e7f3dd86d..4d2007f9fe0 100644 --- a/source/blender/blenlib/BLI_string.h +++ b/source/blender/blenlib/BLI_string.h @@ -85,6 +85,9 @@ int BLI_str_rstrip_float_zero(char *str, const char pad) ATTR_NONNULL(); int BLI_str_index_in_array_n(const char *__restrict str, const char **__restrict str_array, const int str_array_len) ATTR_NONNULL(); int BLI_str_index_in_array(const char *__restrict str, const char **__restrict str_array) ATTR_NONNULL(); +bool BLI_str_ends_with(const char *str,const char *end) ATTR_NONNULL(); +bool BLI_strn_ends_with(const char *str,const char *end, int length) ATTR_NONNULL(); + size_t BLI_str_partition(const char *str, const char delim[], char **sep, char **suf) ATTR_NONNULL(); size_t BLI_str_rpartition(const char *str, const char delim[], char **sep, char **suf) ATTR_NONNULL(); size_t BLI_str_partition_ex(const char *str, const char delim[], char **sep, char **suf, const bool from_right) ATTR_NONNULL(); diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c index 765e2ea127b..365e555f9a9 100644 --- a/source/blender/blenlib/intern/string.c +++ b/source/blender/blenlib/intern/string.c @@ -773,6 +773,35 @@ int BLI_str_index_in_array(const char *__restrict str, const char **__restrict s return -1; } +bool BLI_strn_ends_with(const char *str,const char *end, int slength) +{ + size_t elength = strlen(end); + + if (elength < slength) { + const char *iter = str + slength - elength; + while (*iter) { + if (*iter++ != *end++) { + return false; + } + } + return true; + } + return false; +} + +/** + * Find if a string ends with another string. + * + * \param str The string to search within. + * \param end The string we look for at the end. + * \return If str ends with end. + */ +bool BLI_str_ends_with(const char *str,const char *end) +{ + size_t slength = strlen(str); + return BLI_strn_ends_with(str, end, slength); +} + /** * Find the first char matching one of the chars in \a delim, from left. * diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index d00a64abb9b..ee212d6e9d9 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -43,10 +43,12 @@ #include "DNA_scene_types.h" +#include "BKE_action.h" #include "BKE_fcurve.h" #include "BKE_report.h" #include "BKE_library.h" #include "BKE_global.h" +#include "BKE_deform.h" #include "RNA_access.h" @@ -477,6 +479,7 @@ typedef struct tAnimCopybufItem { BezTriple *bezt; /* keyframes in buffer */ short id_type; /* Result of GS(id->name)*/ + bool is_bone; /* special flag for armature bones */ } tAnimCopybufItem; @@ -540,6 +543,31 @@ short copy_animedit_keys(bAnimContext *ac, ListBase *anim_data) aci->grp = fcu->grp; aci->rna_path = MEM_dupallocN(fcu->rna_path); aci->array_index = fcu->array_index; + + /* detect if this is a bone. We do that here rather than during pasting because ID pointers will get invalidated if we undo. + * storing the relavant information here helps avoiding crashes if we undo-repaste */ + if ((aci->id_type == ID_OB) && (((Object *)aci->id)->type == OB_ARMATURE) && aci->rna_path) { + Object *ob = (Object *)aci->id; + char *str_start; + + if ((str_start = strstr(aci->rna_path, "pose.bones["))) { + bPoseChannel *pchan; + int length = 0; + char *str_end; + + str_start += 12; + str_end = strchr(str_start, '\"'); + length = str_end - str_start; + str_start[length] = 0; + pchan = BKE_pose_channel_find_name(ob->pose, str_start); + str_start[length] = '\"'; + + if (pchan) { + aci->is_bone = true; + } + } + } + BLI_addtail(&animcopybuf, aci); /* add selected keyframes to buffer */ @@ -587,19 +615,64 @@ short copy_animedit_keys(bAnimContext *ac, ListBase *anim_data) return 0; } +static void flip_names(tAnimCopybufItem *aci, char **name) { + if (aci->is_bone) { + char *str_start; + if ((str_start = strstr(aci->rna_path, "pose.bones["))) { + /* ninja coding, try to change the name */ + char bname_new[MAX_VGROUP_NAME]; + char *str_iter, *str_end; + int length, prefix_l, postfix_l; + + str_start += 12; + prefix_l = str_start - aci->rna_path; + + str_end = strchr(str_start, '\"'); + + length = str_end - str_start; + postfix_l = strlen(str_end); + + /* more ninja stuff, temporary substitute with NULL terminator */ + str_start[length] = 0; + BKE_deform_flip_side_name(bname_new, str_start, false); + str_start[length] = '\"'; + + str_iter = *name = MEM_mallocN(sizeof(char) * (prefix_l + postfix_l + length + 1), "flipped_path"); + + BLI_strncpy(str_iter, aci->rna_path, prefix_l + 1); + str_iter += prefix_l ; + BLI_strncpy(str_iter, bname_new, length + 1); + str_iter += length; + BLI_strncpy(str_iter, str_end, postfix_l + 1); + str_iter[postfix_l] = 0; + } + } +} + /* ------------------- */ /* most strict method: exact matches only */ -static tAnimCopybufItem *pastebuf_match_path_full(FCurve *fcu, const short from_single, const short to_simple) +static tAnimCopybufItem *pastebuf_match_path_full(FCurve *fcu, const short from_single, const short to_simple, bool flip) { tAnimCopybufItem *aci; for (aci = animcopybuf.first; aci; aci = aci->next) { - /* check that paths exist */ if (to_simple || (aci->rna_path && fcu->rna_path)) { - if (to_simple || (strcmp(aci->rna_path, fcu->rna_path) == 0)) { - if ((from_single) || (aci->array_index == fcu->array_index)) + if (!to_simple && flip && aci->is_bone && fcu->rna_path) { + if ((from_single) || (aci->array_index == fcu->array_index)) { + char *name = NULL; + flip_names(aci, &name); + if (strcmp(name, fcu->rna_path) == 0) { + MEM_freeN(name); + break; + } + MEM_freeN(name); + } + } + else if (to_simple || (strcmp(aci->rna_path, fcu->rna_path) == 0)) { + if ((from_single) || (aci->array_index == fcu->array_index)) { break; + } } } } @@ -670,8 +743,29 @@ static tAnimCopybufItem *pastebuf_match_index_only(FCurve *fcu, const short from /* ................ */ +static void do_curve_mirror_flippping(tAnimCopybufItem *aci, BezTriple *bezt) { + if (aci->is_bone) { + int slength = strlen(aci->rna_path); + bool flip = false; + if (BLI_strn_ends_with(aci->rna_path, "location", slength) && aci->array_index == 0) + flip = true; + else if (BLI_strn_ends_with(aci->rna_path, "rotation_quaternion", slength) && ELEM(aci->array_index, 2, 3)) + flip = true; + else if (BLI_strn_ends_with(aci->rna_path, "rotation_euler", slength) && ELEM(aci->array_index, 1, 2)) + flip = true; + else if (BLI_strn_ends_with(aci->rna_path, "rotation_axis_angle", slength) && ELEM(aci->array_index, 2, 3)) + flip = true; + + if (flip) { + bezt->vec[0][1] = -bezt->vec[0][1]; + bezt->vec[1][1] = -bezt->vec[1][1]; + bezt->vec[2][1] = -bezt->vec[2][1]; + } + } +} + /* helper for paste_animedit_keys() - performs the actual pasting */ -static void paste_animedit_keys_fcurve(FCurve *fcu, tAnimCopybufItem *aci, float offset, const eKeyMergeMode merge_mode) +static void paste_animedit_keys_fcurve(FCurve *fcu, tAnimCopybufItem *aci, float offset, const eKeyMergeMode merge_mode, bool flip) { BezTriple *bezt; int i; @@ -726,6 +820,9 @@ static void paste_animedit_keys_fcurve(FCurve *fcu, tAnimCopybufItem *aci, float /* just start pasting, with the first keyframe on the current frame, and so on */ for (i = 0, bezt = aci->bezt; i < aci->totvert; i++, bezt++) { /* temporarily apply offset to src beztriple while copying */ + if (flip) + do_curve_mirror_flippping(aci, bezt); + bezt->vec[0][0] += offset; bezt->vec[1][0] += offset; bezt->vec[2][0] += offset; @@ -733,12 +830,16 @@ static void paste_animedit_keys_fcurve(FCurve *fcu, tAnimCopybufItem *aci, float /* insert the keyframe * NOTE: we do not want to inherit handles from existing keyframes in this case! */ - insert_bezt_fcurve(fcu, bezt, INSERTKEY_OVERWRITE_FULL); + insert_bezt_fcurve(fcu, bezt, INSERTKEY_OVERWRITE_FULL); + /* un-apply offset from src beztriple after copying */ bezt->vec[0][0] -= offset; bezt->vec[1][0] -= offset; bezt->vec[2][0] -= offset; + + if (flip) + do_curve_mirror_flippping(aci, bezt); } /* recalculate F-Curve's handles? */ @@ -768,7 +869,7 @@ EnumPropertyItem keyframe_paste_merge_items[] = { * \return Status code is whether the method FAILED to do anything */ short paste_animedit_keys(bAnimContext *ac, ListBase *anim_data, - const eKeyPasteOffset offset_mode, const eKeyMergeMode merge_mode) + const eKeyPasteOffset offset_mode, const eKeyMergeMode merge_mode, bool flip) { bAnimListElem *ale; @@ -816,7 +917,7 @@ short paste_animedit_keys(bAnimContext *ac, ListBase *anim_data, fcu = (FCurve *)ale->data; /* destination F-Curve */ aci = animcopybuf.first; - paste_animedit_keys_fcurve(fcu, aci, offset, merge_mode); + paste_animedit_keys_fcurve(fcu, aci, offset, merge_mode, false); } else { /* from selected channels @@ -839,7 +940,7 @@ short paste_animedit_keys(bAnimContext *ac, ListBase *anim_data, switch (pass) { case 0: /* most strict, must be exact path match data_path & index */ - aci = pastebuf_match_path_full(fcu, from_single, to_simple); + aci = pastebuf_match_path_full(fcu, from_single, to_simple, flip); break; case 1: @@ -856,7 +957,7 @@ short paste_animedit_keys(bAnimContext *ac, ListBase *anim_data, /* copy the relevant data from the matching buffer curve */ if (aci) { totmatch++; - paste_animedit_keys_fcurve(fcu, aci, offset, merge_mode); + paste_animedit_keys_fcurve(fcu, aci, offset, merge_mode, flip); } ale->update |= ANIM_UPDATE_DEFAULT; diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h index e9b22e49ac2..adf82acb399 100644 --- a/source/blender/editors/include/ED_keyframes_edit.h +++ b/source/blender/editors/include/ED_keyframes_edit.h @@ -270,7 +270,7 @@ void sample_fcurve(struct FCurve *fcu); void free_anim_copybuf(void); short copy_animedit_keys(struct bAnimContext *ac, ListBase *anim_data); short paste_animedit_keys(struct bAnimContext *ac, ListBase *anim_data, - const eKeyPasteOffset offset_mode, const eKeyMergeMode merge_mode); + const eKeyPasteOffset offset_mode, const eKeyMergeMode merge_mode, bool flip); /* ************************************************ */ diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 47a29426192..bc9c578b558 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -545,7 +545,7 @@ static short copy_action_keys(bAnimContext *ac) static short paste_action_keys(bAnimContext *ac, - const eKeyPasteOffset offset_mode, const eKeyMergeMode merge_mode) + const eKeyPasteOffset offset_mode, const eKeyMergeMode merge_mode, bool flip) { ListBase anim_data = {NULL, NULL}; int filter, ok = 0; @@ -562,7 +562,7 @@ static short paste_action_keys(bAnimContext *ac, ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* paste keyframes */ - ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode); + ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode, flip); /* clean up */ ANIM_animdata_freelist(&anim_data); @@ -622,6 +622,7 @@ static int actkeys_paste_exec(bContext *C, wmOperator *op) const eKeyPasteOffset offset_mode = RNA_enum_get(op->ptr, "offset"); const eKeyMergeMode merge_mode = RNA_enum_get(op->ptr, "merge"); + const bool flipped = RNA_boolean_get(op->ptr, "flipped"); /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) @@ -638,7 +639,7 @@ static int actkeys_paste_exec(bContext *C, wmOperator *op) } else { /* non-zero return means an error occurred while trying to paste */ - if (paste_action_keys(&ac, offset_mode, merge_mode)) { + if (paste_action_keys(&ac, offset_mode, merge_mode, flipped)) { return OPERATOR_CANCELLED; } } @@ -651,6 +652,7 @@ static int actkeys_paste_exec(bContext *C, wmOperator *op) void ACTION_OT_paste(wmOperatorType *ot) { + PropertyRNA *prop; /* identifiers */ ot->name = "Paste Keyframes"; ot->idname = "ACTION_OT_paste"; @@ -667,6 +669,8 @@ void ACTION_OT_paste(wmOperatorType *ot) /* props */ RNA_def_enum(ot->srna, "offset", keyframe_paste_offset_items, KEYFRAME_PASTE_OFFSET_CFRA_START, "Offset", "Paste time offset of keys"); RNA_def_enum(ot->srna, "merge", keyframe_paste_merge_items, KEYFRAME_PASTE_MERGE_MIX, "Type", "Method of merging pasted keys and existing"); + prop = RNA_def_boolean(ot->srna, "flipped", false, "Flipped", "Paste keyframes from mirrored bones if they exist"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /* ******************** Insert Keyframes Operator ************************* */ diff --git a/source/blender/editors/space_action/action_ops.c b/source/blender/editors/space_action/action_ops.c index 364434ee0ce..0fbacefa8e3 100644 --- a/source/blender/editors/space_action/action_ops.c +++ b/source/blender/editors/space_action/action_ops.c @@ -205,9 +205,13 @@ static void action_keymap_keyframes(wmKeyConfig *keyconf, wmKeyMap *keymap) /* copy/paste */ WM_keymap_add_item(keymap, "ACTION_OT_copy", CKEY, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "ACTION_OT_paste", VKEY, KM_PRESS, KM_CTRL, 0); + kmi = WM_keymap_add_item(keymap, "ACTION_OT_paste", VKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "flipped", true); #ifdef __APPLE__ WM_keymap_add_item(keymap, "ACTION_OT_copy", CKEY, KM_PRESS, KM_OSKEY, 0); WM_keymap_add_item(keymap, "ACTION_OT_paste", VKEY, KM_PRESS, KM_OSKEY, 0); + kmi = WM_keymap_add_item(keymap, "ACTION_OT_paste", VKEY, KM_PRESS, KM_OSKEY | KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "flipped", true); #endif /* auto-set range */ diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 583d4a04ce0..b87f80c4e62 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -697,7 +697,7 @@ static short copy_graph_keys(bAnimContext *ac) } static short paste_graph_keys(bAnimContext *ac, - const eKeyPasteOffset offset_mode, const eKeyMergeMode merge_mode) + const eKeyPasteOffset offset_mode, const eKeyMergeMode merge_mode, bool flip) { ListBase anim_data = {NULL, NULL}; int filter, ok = 0; @@ -714,7 +714,7 @@ static short paste_graph_keys(bAnimContext *ac, ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* paste keyframes */ - ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode); + ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode, flip); /* clean up */ ANIM_animdata_freelist(&anim_data); @@ -765,6 +765,7 @@ static int graphkeys_paste_exec(bContext *C, wmOperator *op) const eKeyPasteOffset offset_mode = RNA_enum_get(op->ptr, "offset"); const eKeyMergeMode merge_mode = RNA_enum_get(op->ptr, "merge"); + const bool flipped = RNA_boolean_get(op->ptr, "flipped"); /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) @@ -774,7 +775,7 @@ static int graphkeys_paste_exec(bContext *C, wmOperator *op) ac.reports = op->reports; /* paste keyframes - non-zero return means an error occurred while trying to paste */ - if (paste_graph_keys(&ac, offset_mode, merge_mode)) { + if (paste_graph_keys(&ac, offset_mode, merge_mode, flipped)) { return OPERATOR_CANCELLED; } @@ -786,6 +787,8 @@ static int graphkeys_paste_exec(bContext *C, wmOperator *op) void GRAPH_OT_paste(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Paste Keyframes"; ot->idname = "GRAPH_OT_paste"; @@ -802,6 +805,8 @@ void GRAPH_OT_paste(wmOperatorType *ot) /* props */ RNA_def_enum(ot->srna, "offset", keyframe_paste_offset_items, KEYFRAME_PASTE_OFFSET_CFRA_START, "Offset", "Paste time offset of keys"); RNA_def_enum(ot->srna, "merge", keyframe_paste_merge_items, KEYFRAME_PASTE_MERGE_MIX, "Type", "Method of merging pasted keys and existing"); + prop = RNA_def_boolean(ot->srna, "flipped", false, "Flipped", "Paste keyframes from mirrored bones if they exist"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /* ******************** Duplicate Keyframes Operator ************************* */ diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c index f06d738200a..da308d0b1f1 100644 --- a/source/blender/editors/space_graph/graph_ops.c +++ b/source/blender/editors/space_graph/graph_ops.c @@ -580,9 +580,13 @@ static void graphedit_keymap_keyframes(wmKeyConfig *keyconf, wmKeyMap *keymap) /* copy/paste */ WM_keymap_add_item(keymap, "GRAPH_OT_copy", CKEY, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "GRAPH_OT_paste", VKEY, KM_PRESS, KM_CTRL, 0); + kmi = WM_keymap_add_item(keymap, "GRAPH_OT_paste", VKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "flipped", true); #ifdef __APPLE__ WM_keymap_add_item(keymap, "GRAPH_OT_copy", CKEY, KM_PRESS, KM_OSKEY, 0); WM_keymap_add_item(keymap, "GRAPH_OT_paste", VKEY, KM_PRESS, KM_OSKEY, 0); + kmi = WM_keymap_add_item(keymap, "GRAPH_OT_paste", VKEY, KM_PRESS, KM_OSKEY | KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "flipped", true); #endif /* auto-set range */ |