diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index 69cf1f1edf6..2f268c2c438 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -366,24 +366,22 @@ int BKE_fcurve_pathcache_find_array(struct FCurvePathCache *fcache, int fcurve_result_len); /** - * Calculate the extents of F-Curve's keyframes. + * Calculate the x range of the given F-Curve's data. + * \return true if a range has been found. */ -bool BKE_fcurve_calc_range( - const struct FCurve *fcu, float *min, float *max, bool do_sel_only, bool do_min_length); +bool BKE_fcurve_calc_range(const struct FCurve *fcu, float *r_min, float *r_max, bool selected_keys_only); /** - * Calculate the extents of F-Curve's data. - * \param range Only calculate the bounds of the FCurve in the given range. + * Calculate the x and y extents of F-Curve's data. + * \param frame_range Only calculate the bounds of the FCurve in the given range. * Does the full range if NULL. + * \return true if the bounds have been found. */ bool BKE_fcurve_calc_bounds(const struct FCurve *fcu, - float *xmin, - float *xmax, - float *ymin, - float *ymax, - bool do_sel_only, + bool selected_keys_only, bool include_handles, - const float range[2]); + const float frame_range[2], + struct rctf *r_bounds); /** * Return an array of keyed frames, rounded to `interval`. diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 8549f2df1dc..dea3319ac68 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -1408,7 +1408,7 @@ void calc_action_range(const bAction *act, float *start, float *end, short incl_ * single-keyframe curves will increase the overall length by * a phantom frame (#50354) */ - BKE_fcurve_calc_range(fcu, &nmin, &nmax, false, false); + BKE_fcurve_calc_range(fcu, &nmin, &nmax, false); /* compare to the running tally */ min = min_ff(min, nmin); diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index c5bfe550b52..3b87d4d996e 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -556,252 +556,236 @@ int BKE_fcurve_bezt_binarysearch_index(const BezTriple array[], /* ...................................... */ -/* Helper for calc_fcurve_* functions -> find first and last BezTriple to be used. */ -static bool get_fcurve_end_keyframes(const FCurve *fcu, - BezTriple **first, - BezTriple **last, - const bool do_sel_only, - const float range[2]) +/* Get the first and last index to the bezt array that satisfies the given parameters. + * \param selected_keys_only Only accept indices of bezt that are selected. Is a subset of + * frame_range. \param frame_range Only consider keyframes in that frame interval. Can be NULL. + */ +static bool get_bounding_bezt_indices(const FCurve *fcu, + const bool selected_keys_only, + const float frame_range[2], + int *r_first, + int *r_last) { - bool found = false; - - /* Init outputs. */ - *first = NULL; - *last = NULL; - /* Sanity checks. */ if (fcu->bezt == NULL) { - return found; + return false; } - int first_index = 0; - int last_index = fcu->totvert - 1; + *r_first = 0; + *r_last = fcu->totvert - 1; - if (range != NULL) { + bool found = false; + if (frame_range != NULL) { /* If a range is passed in find the first and last keyframe within that range. */ bool replace = false; - first_index = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, range[0], fcu->totvert, &replace); - last_index = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, range[1], fcu->totvert, &replace); + *r_first = BKE_fcurve_bezt_binarysearch_index( + fcu->bezt, frame_range[0], fcu->totvert, &replace); + *r_last = BKE_fcurve_bezt_binarysearch_index( + fcu->bezt, frame_range[1], fcu->totvert, &replace); /* If first and last index are the same, no keyframes were found in the range. */ - if (first_index == last_index) { - return found; + if (*r_first == *r_last) { + return false; } /* The binary search returns an index where a keyframe would be inserted, * so it needs to be clamped to ensure it is in range of the array. */ - first_index = clamp_i(first_index, 0, fcu->totvert - 1); - last_index = clamp_i(last_index - 1, 0, fcu->totvert - 1); + *r_first = clamp_i(*r_first, 0, fcu->totvert - 1); + *r_last = clamp_i(*r_last - 1, 0, fcu->totvert - 1); } /* Only include selected items? */ - if (do_sel_only) { + if (selected_keys_only) { /* Find first selected. */ - for (int i = first_index; i <= last_index; i++) { + for (int i = *r_first; i <= *r_last; i++) { BezTriple *bezt = &fcu->bezt[i]; if (BEZT_ISSEL_ANY(bezt)) { - *first = bezt; + *r_first = i; found = true; break; } } /* Find last selected. */ - for (int i = last_index; i >= first_index; i--) { + for (int i = *r_last; i >= *r_first; i--) { BezTriple *bezt = &fcu->bezt[i]; if (BEZT_ISSEL_ANY(bezt)) { - *last = bezt; + *r_last = i; found = true; break; } } } else { - *first = &fcu->bezt[first_index]; - *last = &fcu->bezt[last_index]; found = true; } return found; } -bool BKE_fcurve_calc_bounds(const FCurve *fcu, - float *xmin, - float *xmax, - float *ymin, - float *ymax, - const bool do_sel_only, - const bool include_handles, - const float range[2]) +static void calculate_bezt_bounds_x(BezTriple *bezt_array, + const int index_range[2], + const bool include_handles, + float *r_min, + float *r_max) { - float xminv = 999999999.0f, xmaxv = -999999999.0f; - float yminv = 999999999.0f, ymaxv = -999999999.0f; - bool foundvert = false; + *r_min = bezt_array[index_range[0]].vec[1][0]; + *r_max = bezt_array[index_range[1]].vec[1][0]; - const bool use_range = range != NULL; - - if (fcu->totvert) { - if (fcu->bezt) { - - if (xmin || xmax) { - BezTriple *bezt_first = NULL, *bezt_last = NULL; - foundvert = get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only, range); - - if (bezt_first) { - BLI_assert(bezt_last != NULL); - foundvert = true; - if (include_handles) { - xminv = min_fff(xminv, bezt_first->vec[0][0], bezt_first->vec[1][0]); - xmaxv = max_fff(xmaxv, bezt_last->vec[1][0], bezt_last->vec[2][0]); - } - else { - xminv = min_ff(xminv, bezt_first->vec[1][0]); - xmaxv = max_ff(xmaxv, bezt_last->vec[1][0]); - } - } - } - - /* Only loop over keyframes to find extents for values if needed. */ - if (ymin || ymax) { - BezTriple *bezt, *prevbezt = NULL; - - int i; - for (bezt = fcu->bezt, i = 0; i < fcu->totvert; prevbezt = bezt, bezt++, i++) { - if (use_range && (bezt->vec[1][0] < range[0] || bezt->vec[1][0] > range[1])) { - continue; - } - if ((do_sel_only == false) || BEZT_ISSEL_ANY(bezt)) { - /* Keyframe itself. */ - yminv = min_ff(yminv, bezt->vec[1][1]); - ymaxv = max_ff(ymaxv, bezt->vec[1][1]); - - if (include_handles) { - /* Left handle - only if applicable. - * NOTE: for the very first keyframe, - * the left handle actually has no bearings on anything. */ - if (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ)) { - yminv = min_ff(yminv, bezt->vec[0][1]); - ymaxv = max_ff(ymaxv, bezt->vec[0][1]); - } - - /* Right handle - only if applicable. */ - if (bezt->ipo == BEZT_IPO_BEZ) { - yminv = min_ff(yminv, bezt->vec[2][1]); - ymaxv = max_ff(ymaxv, bezt->vec[2][1]); - } - } - - foundvert = true; - } - } - } - } - else if (fcu->fpt) { - /* Frame range can be directly calculated from end verts. */ - if (xmin || xmax) { - xminv = min_ff(xminv, fcu->fpt[0].vec[0]); - xmaxv = max_ff(xmaxv, fcu->fpt[fcu->totvert - 1].vec[0]); - } - - /* Only loop over keyframes to find extents for values if needed. */ - if (ymin || ymax) { - int i = 0; - - for (FPoint *fpt = fcu->fpt; i < fcu->totvert; fpt++, i++) { - if (fpt->vec[1] < yminv) { - yminv = fpt->vec[1]; - } - if (fpt->vec[1] > ymaxv) { - ymaxv = fpt->vec[1]; - } - - foundvert = true; - } - } + if (include_handles) { + /* Need to check all handles because they might extend beyond their neighboring keys. */ + for (int i = index_range[0]; i <= index_range[1]; i++) { + const BezTriple *bezt = &bezt_array[i]; + *r_min = min_fff(*r_min, bezt->vec[0][0], bezt->vec[1][0]); + *r_max = max_fff(*r_max, bezt->vec[1][0], bezt->vec[2][0]); } } - - if (foundvert) { - if (xmin) { - *xmin = xminv; - } - if (xmax) { - *xmax = xmaxv; - } - - if (ymin) { - *ymin = yminv; - } - if (ymax) { - *ymax = ymaxv; - } - } - else { - if (G.debug & G_DEBUG) { - printf("F-Curve calc bounds didn't find anything, so assuming minimum bounds of 1.0\n"); - } - - if (xmin) { - *xmin = use_range ? range[0] : 0.0f; - } - if (xmax) { - *xmax = use_range ? range[1] : 1.0f; - } - - if (ymin) { - *ymin = 0.0f; - } - if (ymax) { - *ymax = 1.0f; - } - } - - return foundvert; } -bool BKE_fcurve_calc_range( - const FCurve *fcu, float *start, float *end, const bool do_sel_only, const bool do_min_length) +static void calculate_bezt_bounds_y(BezTriple *bezt_array, + const int index_range[2], + const bool selected_keys_only, + const bool include_handles, + float *r_min, + float *r_max) { - float min = 999999999.0f, max = -999999999.0f; + *r_min = bezt_array[index_range[0]].vec[1][1]; + *r_max = bezt_array[index_range[0]].vec[1][1]; + + for (int i = index_range[0]; i <= index_range[1]; i++) { + const BezTriple *bezt = &bezt_array[i]; + + if (selected_keys_only && !BEZT_ISSEL_ANY(bezt)) { + continue; + } + + *r_min = min_ff(*r_min, bezt->vec[1][1]); + *r_max = max_ff(*r_max, bezt->vec[1][1]); + + if (include_handles) { + *r_min = min_fff(*r_min, bezt->vec[0][1], bezt->vec[2][1]); + *r_max = max_fff(*r_max, bezt->vec[0][1], bezt->vec[2][1]); + } + } +} + +static bool calculate_bezt_bounds(const FCurve *fcu, + const bool selected_keys_only, + const bool include_handles, + const float frame_range[2], + rctf *r_bounds) +{ + int index_range[2]; + const bool found_indices = get_bounding_bezt_indices( + fcu, selected_keys_only, frame_range, &index_range[0], &index_range[1]); + if (!found_indices) { + return false; + } + calculate_bezt_bounds_x( + fcu->bezt, index_range, include_handles, &r_bounds->xmin, &r_bounds->xmax); + calculate_bezt_bounds_y(fcu->bezt, + index_range, + selected_keys_only, + include_handles, + &r_bounds->ymin, + &r_bounds->ymax); + return true; +} + +static bool calculate_fpt_bounds(const FCurve *fcu, const float frame_range[2], rctf *r_bounds) +{ + r_bounds->xmin = INFINITY; + r_bounds->xmax = -INFINITY; + r_bounds->ymin = INFINITY; + r_bounds->ymax = -INFINITY; + + const int first_index = 0; + const int last_index = fcu->totvert - 1; + int start_index = first_index; + int end_index = last_index; + + if (frame_range != NULL) { + /* Start index can be calculated because fpt has a key on every full frame. */ + const float start_index_f = frame_range[0] - fcu->fpt[0].vec[0]; + const float end_index_f = start_index_f + frame_range[1] - frame_range[0]; + + if (start_index_f > fcu->totvert - 1 || end_index_f < 0) { + /* Range is outside of keyframe samples. */ + return false; + } + + /* Range might be partially covering keyframe samples. */ + start_index = clamp_i(start_index_f, 0, fcu->totvert - 1); + end_index = clamp_i(end_index_f, 0, fcu->totvert - 1); + } + + /* X range can be directly calculated from end verts. */ + r_bounds->xmin = fcu->fpt[start_index].vec[0]; + r_bounds->xmax = fcu->fpt[end_index].vec[0]; + + for (int i = start_index; i <= end_index; i++) { + r_bounds->ymin = min_ff(r_bounds->ymin, fcu->fpt[i].vec[1]); + r_bounds->ymax = max_ff(r_bounds->ymax, fcu->fpt[i].vec[1]); + } + + return BLI_rctf_is_valid(r_bounds); +} + +bool BKE_fcurve_calc_bounds(const FCurve *fcu, + const bool selected_keys_only, + const bool include_handles, + const float frame_range[2], + rctf *r_bounds) +{ + if (fcu->totvert == 0) { + return false; + } + + if (fcu->bezt) { + const bool found_bounds = calculate_bezt_bounds( + fcu, selected_keys_only, include_handles, frame_range, r_bounds); + return found_bounds; + } + + if (fcu->fpt) { + const bool founds_bounds = calculate_fpt_bounds(fcu, frame_range, r_bounds); + return founds_bounds; + } + + return false; +} + +bool BKE_fcurve_calc_range(const FCurve *fcu, + float *r_start, + float *r_end, + const bool selected_keys_only) +{ + float min, max = 0.0f; bool foundvert = false; - if (fcu->totvert) { - if (fcu->bezt) { - BezTriple *bezt_first = NULL, *bezt_last = NULL; - - /* Get endpoint keyframes. */ - get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only, NULL); - - if (bezt_first) { - BLI_assert(bezt_last != NULL); - - min = min_ff(min, bezt_first->vec[1][0]); - max = max_ff(max, bezt_last->vec[1][0]); - - foundvert = true; - } - } - else if (fcu->fpt) { - min = min_ff(min, fcu->fpt[0].vec[0]); - max = max_ff(max, fcu->fpt[fcu->totvert - 1].vec[0]); - - foundvert = true; - } + if (fcu->totvert == 0) { + return false; } - if (foundvert == false) { - min = max = 0.0f; - } - - if (do_min_length) { - /* Minimum length is 1 frame. */ - if (min == max) { - max += 1.0f; + if (fcu->bezt) { + int index_range[2]; + foundvert = get_bounding_bezt_indices( + fcu, selected_keys_only, NULL, &index_range[0], &index_range[1]); + if (!foundvert) { + return false; } + const bool include_handles = false; + calculate_bezt_bounds_x(fcu->bezt, index_range, include_handles, &min, &max); + } + else if (fcu->fpt) { + min = fcu->fpt[0].vec[0]; + max = fcu->fpt[fcu->totvert - 1].vec[0]; + + foundvert = true; } - *start = min; - *end = max; + *r_start = min; + *r_end = max; return foundvert; } diff --git a/source/blender/blenkernel/intern/fcurve_test.cc b/source/blender/blenkernel/intern/fcurve_test.cc index 66c4477ebc2..e95ef50dd57 100644 --- a/source/blender/blenkernel/intern/fcurve_test.cc +++ b/source/blender/blenkernel/intern/fcurve_test.cc @@ -346,4 +346,189 @@ TEST(BKE_fcurve, BKE_fcurve_keyframe_move_value_with_handles) BKE_fcurve_free(fcu); } +TEST(BKE_fcurve, BKE_fcurve_calc_range) +{ + FCurve *fcu = BKE_fcurve_create(); + + insert_vert_fcurve(fcu, 1.0f, 7.5f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF); + insert_vert_fcurve(fcu, 4.0f, -15.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF); + insert_vert_fcurve(fcu, 8.0f, 15.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF); + insert_vert_fcurve(fcu, 14.0f, 8.2f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF); + insert_vert_fcurve(fcu, 18.2f, -20.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF); + + for (int i = 0; i < fcu->totvert; i++) { + fcu->bezt[i].f1 &= ~SELECT; + fcu->bezt[i].f2 &= ~SELECT; + fcu->bezt[i].f3 &= ~SELECT; + } + + float min, max; + bool success; + + /* All keys. */ + success = BKE_fcurve_calc_range(fcu, &min, &max, false); + EXPECT_TRUE(success) << "A non-empty FCurve should have a range."; + EXPECT_FLOAT_EQ(fcu->bezt[0].vec[1][0], min); + EXPECT_FLOAT_EQ(fcu->bezt[4].vec[1][0], max); + + /* Only selected. */ + success = BKE_fcurve_calc_range(fcu, &min, &max, true); + EXPECT_FALSE(success) + << "Using selected keyframes only should not find a range if nothing is selected."; + + fcu->bezt[1].f2 |= SELECT; + fcu->bezt[3].f2 |= SELECT; + + success = BKE_fcurve_calc_range(fcu, &min, &max, true); + EXPECT_TRUE(success) << "Range of selected keyframes should have been found."; + EXPECT_FLOAT_EQ(fcu->bezt[1].vec[1][0], min); + EXPECT_FLOAT_EQ(fcu->bezt[3].vec[1][0], max); + + /* Curve samples. */ + const int sample_start = 1; + const int sample_end = 20; + fcurve_store_samples(fcu, NULL, sample_start, sample_end, fcurve_samplingcb_evalcurve); + + success = BKE_fcurve_calc_range(fcu, &min, &max, true); + EXPECT_TRUE(success) << "FCurve samples should have a range."; + + EXPECT_FLOAT_EQ(sample_start, min); + EXPECT_FLOAT_EQ(sample_end, max); + + BKE_fcurve_free(fcu); +} + +TEST(BKE_fcurve, BKE_fcurve_calc_bounds) +{ + FCurve *fcu = BKE_fcurve_create(); + + insert_vert_fcurve(fcu, 1.0f, 7.5f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF); + insert_vert_fcurve(fcu, 4.0f, -15.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF); + insert_vert_fcurve(fcu, 8.0f, 15.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF); + insert_vert_fcurve(fcu, 14.0f, 8.2f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF); + insert_vert_fcurve(fcu, 18.2f, -20.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF); + + for (int i = 0; i < fcu->totvert; i++) { + fcu->bezt[i].f1 &= ~SELECT; + fcu->bezt[i].f2 &= ~SELECT; + fcu->bezt[i].f3 &= ~SELECT; + } + + fcu->bezt[0].vec[0][0] = -5.0f; + fcu->bezt[4].vec[2][0] = 25.0f; + + rctf bounds; + bool success; + + /* All keys. */ + success = BKE_fcurve_calc_bounds( + fcu, false /* sel only */, false /* include handles */, NULL /* frame range */, &bounds); + EXPECT_TRUE(success) << "A non-empty FCurve should have bounds."; + EXPECT_FLOAT_EQ(fcu->bezt[0].vec[1][0], bounds.xmin); + EXPECT_FLOAT_EQ(fcu->bezt[4].vec[1][0], bounds.xmax); + EXPECT_FLOAT_EQ(fcu->bezt[4].vec[1][1], bounds.ymin); + EXPECT_FLOAT_EQ(fcu->bezt[2].vec[1][1], bounds.ymax); + + /* Only selected. */ + success = BKE_fcurve_calc_bounds( + fcu, true /* sel only */, false /* include handles */, NULL /* frame range */, &bounds); + EXPECT_FALSE(success) + << "Using selected keyframes only should not find bounds if nothing is selected."; + + fcu->bezt[1].f2 |= SELECT; + fcu->bezt[3].f2 |= SELECT; + + success = BKE_fcurve_calc_bounds( + fcu, true /* sel only */, false /* include handles */, NULL /* frame range */, &bounds); + EXPECT_TRUE(success) << "Selected keys should have been found."; + EXPECT_FLOAT_EQ(fcu->bezt[1].vec[1][0], bounds.xmin); + EXPECT_FLOAT_EQ(fcu->bezt[3].vec[1][0], bounds.xmax); + EXPECT_FLOAT_EQ(fcu->bezt[1].vec[1][1], bounds.ymin); + EXPECT_FLOAT_EQ(fcu->bezt[3].vec[1][1], bounds.ymax); + + /* Including handles. */ + success = BKE_fcurve_calc_bounds( + fcu, false /* sel only */, true /* include handles */, NULL /* frame range */, &bounds); + EXPECT_TRUE(success) << "A non-empty FCurve should have bounds including handles."; + EXPECT_FLOAT_EQ(fcu->bezt[0].vec[0][0], bounds.xmin); + EXPECT_FLOAT_EQ(fcu->bezt[4].vec[2][0], bounds.xmax); + EXPECT_FLOAT_EQ(fcu->bezt[4].vec[1][1], bounds.ymin); + EXPECT_FLOAT_EQ(fcu->bezt[2].vec[1][1], bounds.ymax); + + /* Range. */ + float range[2]; + + range[0] = 25; + range[1] = 30; + success = BKE_fcurve_calc_bounds( + fcu, false /* sel only */, false /* include handles */, range /* frame range */, &bounds); + EXPECT_FALSE(success) << "A frame range outside the range of keyframes should not find bounds."; + + range[0] = 0; + range[1] = 18.2f; + success = BKE_fcurve_calc_bounds( + fcu, false /* sel only */, false /* include handles */, range /* frame range */, &bounds); + EXPECT_TRUE(success) << "A frame range within the range of keyframes should find bounds."; + EXPECT_FLOAT_EQ(fcu->bezt[0].vec[1][0], bounds.xmin); + EXPECT_FLOAT_EQ(fcu->bezt[3].vec[1][0], bounds.xmax); + EXPECT_FLOAT_EQ(fcu->bezt[1].vec[1][1], bounds.ymin); + EXPECT_FLOAT_EQ(fcu->bezt[2].vec[1][1], bounds.ymax); + + /* Range and handles. */ + success = BKE_fcurve_calc_bounds( + fcu, false /* sel only */, true /* include handles */, range /* frame range */, &bounds); + EXPECT_TRUE(success) + << "A frame range within the range of keyframes should find bounds with handles."; + EXPECT_FLOAT_EQ(fcu->bezt[0].vec[0][0], bounds.xmin); + EXPECT_FLOAT_EQ(fcu->bezt[3].vec[2][0], bounds.xmax); + EXPECT_FLOAT_EQ(fcu->bezt[1].vec[1][1], bounds.ymin); + EXPECT_FLOAT_EQ(fcu->bezt[2].vec[1][1], bounds.ymax); + + /* Range, handles and only selection. */ + range[0] = 8.0f; + range[1] = 18.2f; + success = BKE_fcurve_calc_bounds( + fcu, true /* sel only */, true /* include handles */, range /* frame range */, &bounds); + EXPECT_TRUE(success) + << "A frame range within the range of keyframes should find bounds of selected keyframes."; + EXPECT_FLOAT_EQ(fcu->bezt[3].vec[0][0], bounds.xmin); + EXPECT_FLOAT_EQ(fcu->bezt[3].vec[2][0], bounds.xmax); + EXPECT_FLOAT_EQ(fcu->bezt[3].vec[2][1], bounds.ymin); + EXPECT_FLOAT_EQ(fcu->bezt[3].vec[0][1], bounds.ymax); + + /* Curve samples. */ + const int sample_start = 1; + const int sample_end = 20; + fcurve_store_samples(fcu, NULL, sample_start, sample_end, fcurve_samplingcb_evalcurve); + + success = BKE_fcurve_calc_bounds( + fcu, false /* sel only */, false /* include handles */, NULL /* frame range */, &bounds); + EXPECT_TRUE(success) << "FCurve samples should have a range."; + + EXPECT_FLOAT_EQ(sample_start, bounds.xmin); + EXPECT_FLOAT_EQ(sample_end, bounds.xmax); + EXPECT_FLOAT_EQ(-20.0f, bounds.ymin); + EXPECT_FLOAT_EQ(15.0f, bounds.ymax); + + range[0] = 8.0f; + range[1] = 20.0f; + success = BKE_fcurve_calc_bounds( + fcu, false /* sel only */, false /* include handles */, range /* frame range */, &bounds); + EXPECT_TRUE(success) << "FCurve samples should have a range."; + + EXPECT_FLOAT_EQ(range[0], bounds.xmin); + EXPECT_FLOAT_EQ(range[1], bounds.xmax); + EXPECT_FLOAT_EQ(-20.0f, bounds.ymin); + EXPECT_FLOAT_EQ(15.0f, bounds.ymax); + + range[0] = 20.1f; + range[1] = 30.0f; + success = BKE_fcurve_calc_bounds( + fcu, false /* sel only */, false /* include handles */, range /* frame range */, &bounds); + EXPECT_FALSE(success) + << "A frame range outside the range of keyframe samples should not have bounds."; + + BKE_fcurve_free(fcu); +} + } // namespace blender::bke::tests diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index 513a9051d22..d4418529c92 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -3651,17 +3651,7 @@ static bool get_normalized_fcurve_bounds(FCurve *fcu, rctf *r_bounds) { const bool fcu_selection_only = false; - const bool found_bounds = BKE_fcurve_calc_bounds(fcu, - &r_bounds->xmin, - &r_bounds->xmax, - &r_bounds->ymin, - &r_bounds->ymax, - fcu_selection_only, - include_handles, - range); - if (!found_bounds) { - return false; - } + BKE_fcurve_calc_bounds(fcu, fcu_selection_only, include_handles, range, r_bounds); const short mapping_flag = ANIM_get_normalization_flags(ac); @@ -3736,8 +3726,8 @@ static void get_view_range(Scene *scene, const bool use_preview_range, float r_r r_range[1] = scene->r.pefra; } else { - r_range[0] = -FLT_MAX; - r_range[1] = FLT_MAX; + r_range[0] = scene->r.sfra; + r_range[1] = scene->r.efra; } } @@ -3879,6 +3869,7 @@ static int graphkeys_channel_view_pick_invoke(bContext *C, wmOperator *op, const float range[2]; const bool use_preview_range = RNA_boolean_get(op->ptr, "use_preview_range"); + get_view_range(ac.scene, use_preview_range, range); rctf bounds; diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 95a407ce98d..bf0a9df62bf 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -203,7 +203,7 @@ static bool get_keyframe_extents(bAnimContext *ac, float *min, float *max, const float tmin, tmax; /* get range and apply necessary scaling before processing */ - if (BKE_fcurve_calc_range(fcu, &tmin, &tmax, onlySel, false)) { + if (BKE_fcurve_calc_range(fcu, &tmin, &tmax, onlySel)) { if (adt) { tmin = BKE_nla_tweakedit_remap(adt, tmin, NLATIME_CONVERT_MAP); diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c index 4a92fd2a651..ff4cc11885c 100644 --- a/source/blender/editors/space_graph/graph_draw.c +++ b/source/blender/editors/space_graph/graph_draw.c @@ -632,7 +632,7 @@ static void draw_fcurve_curve(bAnimContext *ac, if (!draw_extrapolation) { float fcu_start = 0; float fcu_end = 0; - BKE_fcurve_calc_range(fcu_, &fcu_start, &fcu_end, false, false); + BKE_fcurve_calc_range(fcu_, &fcu_start, &fcu_end, false); fcu_start = BKE_nla_tweakedit_remap(adt, fcu_start, NLATIME_CONVERT_MAP); fcu_end = BKE_nla_tweakedit_remap(adt, fcu_end, NLATIME_CONVERT_MAP); diff --git a/source/blender/editors/space_graph/graph_view.c b/source/blender/editors/space_graph/graph_view.c index 6f220c1a5aa..9e25f4282f4 100644 --- a/source/blender/editors/space_graph/graph_view.c +++ b/source/blender/editors/space_graph/graph_view.c @@ -85,40 +85,39 @@ void get_graph_keyframe_extents(bAnimContext *ac, for (ale = anim_data.first; ale; ale = ale->next) { AnimData *adt = ANIM_nla_mapping_get(ac, ale); FCurve *fcu = (FCurve *)ale->key_data; - float txmin, txmax, tymin, tymax; + rctf bounds; float unitFac, offset; /* Get range. */ - if (BKE_fcurve_calc_bounds( - fcu, &txmin, &txmax, &tymin, &tymax, do_sel_only, include_handles, NULL)) { + if (BKE_fcurve_calc_bounds(fcu, do_sel_only, include_handles, NULL, &bounds)) { short mapping_flag = ANIM_get_normalization_flags(ac); /* Apply NLA scaling. */ if (adt) { - txmin = BKE_nla_tweakedit_remap(adt, txmin, NLATIME_CONVERT_MAP); - txmax = BKE_nla_tweakedit_remap(adt, txmax, NLATIME_CONVERT_MAP); + bounds.xmin = BKE_nla_tweakedit_remap(adt, bounds.xmin, NLATIME_CONVERT_MAP); + bounds.xmax = BKE_nla_tweakedit_remap(adt, bounds.xmax, NLATIME_CONVERT_MAP); } /* Apply unit corrections. */ unitFac = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag, &offset); - tymin += offset; - tymax += offset; - tymin *= unitFac; - tymax *= unitFac; + bounds.ymin += offset; + bounds.ymax += offset; + bounds.ymin *= unitFac; + bounds.ymax *= unitFac; /* Try to set cur using these values, if they're more extreme than previously set values. */ - if ((xmin) && (txmin < *xmin)) { - *xmin = txmin; + if ((xmin) && (bounds.xmin < *xmin)) { + *xmin = bounds.xmin; } - if ((xmax) && (txmax > *xmax)) { - *xmax = txmax; + if ((xmax) && (bounds.xmax > *xmax)) { + *xmax = bounds.xmax; } - if ((ymin) && (tymin < *ymin)) { - *ymin = tymin; + if ((ymin) && (bounds.ymin < *ymin)) { + *ymin = bounds.ymin; } - if ((ymax) && (tymax > *ymax)) { - *ymax = tymax; + if ((ymax) && (bounds.ymax > *ymax)) { + *ymax = bounds.ymax; } foundBounds = true; diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index e2b218a86c5..55051b39bd1 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -600,7 +600,7 @@ static void rna_FCurve_group_set(PointerRNA *ptr, /* calculate time extents of F-Curve */ static void rna_FCurve_range(FCurve *fcu, float range[2]) { - BKE_fcurve_calc_range(fcu, range, range + 1, false, false); + BKE_fcurve_calc_range(fcu, range, range + 1, false); } static bool rna_FCurve_is_empty_get(PointerRNA *ptr)