diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index b4de24e3b64..cdade9209b6 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -483,6 +483,18 @@ bool BKE_fcurve_delete_keys_selected(struct FCurve *fcu); */ void BKE_fcurve_delete_keys_all(struct FCurve *fcu); +/** + * Called during transform/snapping to make sure selected keyframes replace + * any other keyframes which may reside on that frame (that is not selected). + * + * \param sel_flag: The flag (bezt.f1/2/3) value to use to determine selection. Usually `SELECT`, + * but may want to use a different one at times (if caller does not operate on + * selection). + */ +void BKE_fcurve_merge_duplicate_keys(struct FCurve *fcu, + const int sel_flag, + const bool use_handle); + /* -------- Curve Sanity -------- */ /** diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 3e772e37177..3c4a068b42a 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -1709,6 +1709,133 @@ void BKE_fcurve_delete_keys_all(FCurve *fcu) fcurve_bezt_free(fcu); } +/* Time + Average value */ +typedef struct tRetainedKeyframe { + struct tRetainedKeyframe *next, *prev; + float frame; /* frame to cluster around */ + float val; /* average value */ + + size_t tot_count; /* number of keyframes that have been averaged */ + size_t del_count; /* number of keyframes of this sort that have been deleted so far */ +} tRetainedKeyframe; + +void BKE_fcurve_merge_duplicate_keys(FCurve *fcu, const int sel_flag, const bool use_handle) +{ + /* NOTE: We assume that all keys are sorted */ + ListBase retained_keys = {NULL, NULL}; + const bool can_average_points = ((fcu->flag & (FCURVE_INT_VALUES | FCURVE_DISCRETE_VALUES)) == + 0); + + /* sanity checks */ + if ((fcu->totvert == 0) || (fcu->bezt == NULL)) { + return; + } + + /* 1) Identify selected keyframes, and average the values on those + * in case there are collisions due to multiple keys getting scaled + * to all end up on the same frame + */ + for (int i = 0; i < fcu->totvert; i++) { + BezTriple *bezt = &fcu->bezt[i]; + + if (BEZT_ISSEL_ANY(bezt)) { + bool found = false; + + /* If there's another selected frame here, merge it */ + for (tRetainedKeyframe *rk = retained_keys.last; rk; rk = rk->prev) { + if (IS_EQT(rk->frame, bezt->vec[1][0], BEZT_BINARYSEARCH_THRESH)) { + rk->val += bezt->vec[1][1]; + rk->tot_count++; + + found = true; + break; + } + if (rk->frame < bezt->vec[1][0]) { + /* Terminate early if have passed the supposed insertion point? */ + break; + } + } + + /* If nothing found yet, create a new one */ + if (found == false) { + tRetainedKeyframe *rk = MEM_callocN(sizeof(tRetainedKeyframe), "tRetainedKeyframe"); + + rk->frame = bezt->vec[1][0]; + rk->val = bezt->vec[1][1]; + rk->tot_count = 1; + + BLI_addtail(&retained_keys, rk); + } + } + } + + if (BLI_listbase_is_empty(&retained_keys)) { + /* This may happen if none of the points were selected... */ + if (G.debug & G_DEBUG) { + printf("%s: nothing to do for FCurve %p (rna_path = '%s')\n", __func__, fcu, fcu->rna_path); + } + return; + } + + /* Compute the average values for each retained keyframe */ + LISTBASE_FOREACH (tRetainedKeyframe *, rk, &retained_keys) { + rk->val = rk->val / (float)rk->tot_count; + } + + /* 2) Delete all keyframes duplicating the "retained keys" found above + * - Most of these will be unselected keyframes + * - Some will be selected keyframes though. For those, we only keep the last one + * (or else everything is gone), and replace its value with the averaged value. + */ + for (int i = fcu->totvert - 1; i >= 0; i--) { + BezTriple *bezt = &fcu->bezt[i]; + + /* Is this keyframe a candidate for deletion? */ + /* TODO: Replace loop with an O(1) lookup instead */ + for (tRetainedKeyframe *rk = retained_keys.last; rk; rk = rk->prev) { + if (IS_EQT(bezt->vec[1][0], rk->frame, BEZT_BINARYSEARCH_THRESH)) { + /* Selected keys are treated with greater care than unselected ones... */ + if (BEZT_ISSEL_ANY(bezt)) { + /* - If this is the last selected key left (based on rk->del_count) ==> UPDATE IT + * (or else we wouldn't have any keyframe left here) + * - Otherwise, there are still other selected keyframes on this frame + * to be merged down still ==> DELETE IT + */ + if (rk->del_count == rk->tot_count - 1) { + /* Update keyframe... */ + if (can_average_points) { + /* TODO: update handles too? */ + bezt->vec[1][1] = rk->val; + } + } + else { + /* Delete Keyframe */ + BKE_fcurve_delete_key(fcu, i); + } + + /* Update count of how many we've deleted + * - It should only matter that we're doing this for all but the last one + */ + rk->del_count++; + } + else { + /* Always delete - Unselected keys don't matter */ + BKE_fcurve_delete_key(fcu, i); + } + + /* Stop the RK search... we've found our match now */ + break; + } + } + } + + /* 3) Recalculate handles */ + testhandles_fcurve(fcu, sel_flag, use_handle); + + /* cleanup */ + BLI_freelistN(&retained_keys); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index 75627370a85..00e7b15c59a 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -538,139 +538,6 @@ bool FrameOnMouseSide(char side, float frame, float cframe) /** \} */ -/* -------------------------------------------------------------------- */ -/** \name Animation Editor - * \{ */ - -/* Time + Average value */ -typedef struct tRetainedKeyframe { - struct tRetainedKeyframe *next, *prev; - float frame; /* frame to cluster around */ - float val; /* average value */ - - size_t tot_count; /* number of keyframes that have been averaged */ - size_t del_count; /* number of keyframes of this sort that have been deleted so far */ -} tRetainedKeyframe; - -void posttrans_fcurve_clean(FCurve *fcu, const int sel_flag, const bool use_handle) -{ - /* NOTE: We assume that all keys are sorted */ - ListBase retained_keys = {NULL, NULL}; - const bool can_average_points = ((fcu->flag & (FCURVE_INT_VALUES | FCURVE_DISCRETE_VALUES)) == - 0); - - /* sanity checks */ - if ((fcu->totvert == 0) || (fcu->bezt == NULL)) { - return; - } - - /* 1) Identify selected keyframes, and average the values on those - * in case there are collisions due to multiple keys getting scaled - * to all end up on the same frame - */ - for (int i = 0; i < fcu->totvert; i++) { - BezTriple *bezt = &fcu->bezt[i]; - - if (BEZT_ISSEL_ANY(bezt)) { - bool found = false; - - /* If there's another selected frame here, merge it */ - for (tRetainedKeyframe *rk = retained_keys.last; rk; rk = rk->prev) { - if (IS_EQT(rk->frame, bezt->vec[1][0], BEZT_BINARYSEARCH_THRESH)) { - rk->val += bezt->vec[1][1]; - rk->tot_count++; - - found = true; - break; - } - if (rk->frame < bezt->vec[1][0]) { - /* Terminate early if have passed the supposed insertion point? */ - break; - } - } - - /* If nothing found yet, create a new one */ - if (found == false) { - tRetainedKeyframe *rk = MEM_callocN(sizeof(tRetainedKeyframe), "tRetainedKeyframe"); - - rk->frame = bezt->vec[1][0]; - rk->val = bezt->vec[1][1]; - rk->tot_count = 1; - - BLI_addtail(&retained_keys, rk); - } - } - } - - if (BLI_listbase_is_empty(&retained_keys)) { - /* This may happen if none of the points were selected... */ - if (G.debug & G_DEBUG) { - printf("%s: nothing to do for FCurve %p (rna_path = '%s')\n", __func__, fcu, fcu->rna_path); - } - return; - } - - /* Compute the average values for each retained keyframe */ - LISTBASE_FOREACH (tRetainedKeyframe *, rk, &retained_keys) { - rk->val = rk->val / (float)rk->tot_count; - } - - /* 2) Delete all keyframes duplicating the "retained keys" found above - * - Most of these will be unselected keyframes - * - Some will be selected keyframes though. For those, we only keep the last one - * (or else everything is gone), and replace its value with the averaged value. - */ - for (int i = fcu->totvert - 1; i >= 0; i--) { - BezTriple *bezt = &fcu->bezt[i]; - - /* Is this keyframe a candidate for deletion? */ - /* TODO: Replace loop with an O(1) lookup instead */ - for (tRetainedKeyframe *rk = retained_keys.last; rk; rk = rk->prev) { - if (IS_EQT(bezt->vec[1][0], rk->frame, BEZT_BINARYSEARCH_THRESH)) { - /* Selected keys are treated with greater care than unselected ones... */ - if (BEZT_ISSEL_ANY(bezt)) { - /* - If this is the last selected key left (based on rk->del_count) ==> UPDATE IT - * (or else we wouldn't have any keyframe left here) - * - Otherwise, there are still other selected keyframes on this frame - * to be merged down still ==> DELETE IT - */ - if (rk->del_count == rk->tot_count - 1) { - /* Update keyframe... */ - if (can_average_points) { - /* TODO: update handles too? */ - bezt->vec[1][1] = rk->val; - } - } - else { - /* Delete Keyframe */ - BKE_fcurve_delete_key(fcu, i); - } - - /* Update count of how many we've deleted - * - It should only matter that we're doing this for all but the last one - */ - rk->del_count++; - } - else { - /* Always delete - Unselected keys don't matter */ - BKE_fcurve_delete_key(fcu, i); - } - - /* Stop the RK search... we've found our match now */ - break; - } - } - } - - /* 3) Recalculate handles */ - testhandles_fcurve(fcu, sel_flag, use_handle); - - /* cleanup */ - BLI_freelistN(&retained_keys); -} - -/** \} */ - /* -------------------------------------------------------------------- */ /** \name Transform Utilities * \{ */ diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index 4798d666d70..c3d2a1d86d9 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -83,15 +83,6 @@ bool transform_mode_use_local_origins(const TransInfo *t); */ void transform_around_single_fallback_ex(TransInfo *t, int data_len_all); void transform_around_single_fallback(TransInfo *t); -/** - * Called during special_aftertrans_update to make sure selected keyframes replace - * any other keyframes which may reside on that frame (that is not selected). - * - * \param sel_flag: The flag (bezt.f1/2/3) value to use to determine selection. Usually `SELECT`, - * but may want to use a different one at times (if caller does not operate on - * selection). - */ -void posttrans_fcurve_clean(struct FCurve *fcu, int sel_flag, bool use_handle); /** * Little helper function for ObjectToTransData used to give certain * constraints (ChildOf, FollowPath, and others that may be added) diff --git a/source/blender/editors/transform/transform_convert_action.c b/source/blender/editors/transform/transform_convert_action.c index 8c6f2baf84a..560cc18d7d6 100644 --- a/source/blender/editors/transform/transform_convert_action.c +++ b/source/blender/editors/transform/transform_convert_action.c @@ -16,6 +16,7 @@ #include "BLI_rect.h" #include "BKE_context.h" +#include "BKE_fcurve.h" #include "BKE_gpencil.h" #include "BKE_key.h" #include "BKE_layer.h" @@ -750,11 +751,13 @@ static void posttrans_action_clean(bAnimContext *ac, bAction *act) if (adt) { ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); - posttrans_fcurve_clean(ale->key_data, SELECT, false); /* only use handles in graph editor */ + BKE_fcurve_merge_duplicate_keys( + ale->key_data, SELECT, false); /* only use handles in graph editor */ ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0); } else { - posttrans_fcurve_clean(ale->key_data, SELECT, false); /* only use handles in graph editor */ + BKE_fcurve_merge_duplicate_keys( + ale->key_data, SELECT, false); /* only use handles in graph editor */ } } @@ -806,11 +809,13 @@ static void special_aftertrans_update__actedit(bContext *C, TransInfo *t) if ((saction->flag & SACTION_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) { if (adt) { ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0); - posttrans_fcurve_clean(fcu, SELECT, false); /* only use handles in graph editor */ + BKE_fcurve_merge_duplicate_keys( + fcu, SELECT, false); /* only use handles in graph editor */ ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0); } else { - posttrans_fcurve_clean(fcu, SELECT, false); /* only use handles in graph editor */ + BKE_fcurve_merge_duplicate_keys( + fcu, SELECT, false); /* only use handles in graph editor */ } } break; diff --git a/source/blender/editors/transform/transform_convert_graph.c b/source/blender/editors/transform/transform_convert_graph.c index 27e6c8a25e1..252c5cc3d37 100644 --- a/source/blender/editors/transform/transform_convert_graph.c +++ b/source/blender/editors/transform/transform_convert_graph.c @@ -1009,11 +1009,11 @@ static void special_aftertrans_update__graph(bContext *C, TransInfo *t) if ((sipo->flag & SIPO_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) { if (adt) { ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0); - posttrans_fcurve_clean(fcu, BEZT_FLAG_TEMP_TAG, use_handle); + BKE_fcurve_merge_duplicate_keys(fcu, BEZT_FLAG_TEMP_TAG, use_handle); ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0); } else { - posttrans_fcurve_clean(fcu, BEZT_FLAG_TEMP_TAG, use_handle); + BKE_fcurve_merge_duplicate_keys(fcu, BEZT_FLAG_TEMP_TAG, use_handle); } } }