Animation: add BKE_fcurves_calc_keyed_frames utility

This function returns an array of keyed frames with rounding,
to avoid duplicates caused by subtle floating point difference.

Reviewed By: sybren

Ref D10781
This commit is contained in:
Campbell Barton
2021-03-26 13:06:25 +11:00
parent 758c2210ae
commit 6fd799c72c
2 changed files with 66 additions and 0 deletions

View File

@@ -245,6 +245,14 @@ bool BKE_fcurve_calc_bounds(struct FCurve *fcu,
const bool do_sel_only,
const bool include_handles);
float *BKE_fcurves_calc_keyed_frames_ex(struct FCurve **fcurve_array,
const int fcurve_array_len,
const float interval,
int *r_frames_len);
float *BKE_fcurves_calc_keyed_frames(struct FCurve **fcurve_array,
const int fcurve_array_len,
int *r_frames_len);
void BKE_fcurve_active_keyframe_set(struct FCurve *fcu, const struct BezTriple *active_bezt);
int BKE_fcurve_active_keyframe_index(const struct FCurve *fcu);

View File

@@ -35,7 +35,9 @@
#include "BLI_blenlib.h"
#include "BLI_easing.h"
#include "BLI_ghash.h"
#include "BLI_math.h"
#include "BLI_sort_utils.h"
#include "BKE_anim_data.h"
#include "BKE_animsys.h"
@@ -290,6 +292,12 @@ FCurve *BKE_fcurve_find(ListBase *list, const char rna_path[], const int array_i
return NULL;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name FCurve Iteration
* \{ */
/* Quick way to loop over all fcurves of a given 'path'. */
FCurve *BKE_fcurve_iter_step(FCurve *fcu_iter, const char rna_path[])
{
@@ -829,6 +837,56 @@ bool BKE_fcurve_calc_range(
return foundvert;
}
/**
* Return an array of keyed frames, rounded to `interval`.
*
* \param interval: Set to 1.0 to round to whole keyframes, 0.5 for in-between key-frames, etc.
*
* \note An interval of zero could be supported (this implies no rounding at all),
* however this risks very small differences in float values being treated as separate keyframes.
*/
float *BKE_fcurves_calc_keyed_frames_ex(FCurve **fcurve_array,
int fcurve_array_len,
const float interval,
int *r_frames_len)
{
/* Use `1e-3f` as the smallest possible value since these are converted to integers
* and we can be sure `MAXFRAME / 1e-3f < INT_MAX` as it's around half the size. */
const double interval_db = max_ff(interval, 1e-3f);
GSet *frames_unique = BLI_gset_int_new(__func__);
for (int fcurve_index = 0; fcurve_index < fcurve_array_len; fcurve_index++) {
const FCurve *fcu = fcurve_array[fcurve_index];
for (int i = 0; i < fcu->totvert; i++) {
const BezTriple *bezt = &fcu->bezt[i];
const double value = round((double)bezt->vec[1][0] / interval_db);
BLI_assert(value > INT_MIN && value < INT_MAX);
BLI_gset_add(frames_unique, POINTER_FROM_INT((int)value));
}
}
const size_t frames_len = BLI_gset_len(frames_unique);
float *frames = MEM_mallocN(sizeof(*frames) * frames_len, __func__);
GSetIterator gs_iter;
int i = 0;
GSET_ITER_INDEX (gs_iter, frames_unique, i) {
const int value = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
frames[i] = (double)value * interval_db;
}
BLI_gset_free(frames_unique, NULL);
qsort(frames, frames_len, sizeof(*frames), BLI_sortutil_cmp_float);
*r_frames_len = frames_len;
return frames;
}
float *BKE_fcurves_calc_keyed_frames(FCurve **fcurve_array,
int fcurve_array_len,
int *r_frames_len)
{
return BKE_fcurves_calc_keyed_frames_ex(fcurve_array, fcurve_array_len, 1.0f, r_frames_len);
}
/** \} */
/* -------------------------------------------------------------------- */