From d21fbce9ff43a33a10e2939208f9aeb1c08080d9 Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Fri, 24 Feb 2023 12:45:59 +0100 Subject: [PATCH 01/12] refactor fcurve bounds functions --- source/blender/blenkernel/BKE_fcurve.h | 17 +- source/blender/blenkernel/intern/action.c | 2 +- source/blender/blenkernel/intern/fcurve.c | 335 ++++++++---------- .../editors/animation/anim_channels_edit.c | 12 +- .../editors/space_action/action_edit.c | 2 +- .../blender/editors/space_graph/graph_draw.c | 2 +- .../blender/editors/space_graph/graph_view.c | 33 +- source/blender/makesrna/intern/rna_fcurve.c | 2 +- 8 files changed, 187 insertions(+), 218 deletions(-) diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index 69cf1f1edf6..221114523dc 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -366,24 +366,21 @@ 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. */ -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 *min, float *max, bool do_sel_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 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 8e054d4d35f..1789811b99e 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -556,248 +556,225 @@ 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 do_sel_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 do_sel_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) { /* 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; - - 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 (foundvert) { - if (xmin) { - *xmin = xminv; - } - if (xmax) { - *xmax = xmaxv; - } - - if (ymin) { - *ymin = yminv; - } - if (ymax) { - *ymax = ymaxv; + 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++) { + 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]); } } 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; - } + *r_min = bezt_array[index_range[0]].vec[1][0]; + *r_max = bezt_array[index_range[1]].vec[1][0]; } - - 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 do_sel_only, + const bool include_handles, + float *r_min, + float *r_max) { - float min = 999999999.0f, max = -999999999.0f; - bool foundvert = false; + *r_min = bezt_array[index_range[0]].vec[1][1]; + *r_max = bezt_array[index_range[0]].vec[1][1]; - if (fcu->totvert) { - if (fcu->bezt) { - BezTriple *bezt_first = NULL, *bezt_last = NULL; + BezTriple *bezt, *prev_bezt = NULL; + for (int i = index_range[0]; i <= index_range[1]; i++) { + bezt = &bezt_array[i]; - /* Get endpoint keyframes. */ - get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only, NULL); + if (do_sel_only && !BEZT_ISSEL_ANY(bezt)) { + continue; + } - if (bezt_first) { - BLI_assert(bezt_last != NULL); + *r_min = min_ff(*r_min, bezt->vec[1][1]); + *r_max = max_ff(*r_max, bezt->vec[1][1]); - min = min_ff(min, bezt_first->vec[1][0]); - max = max_ff(max, bezt_last->vec[1][0]); + if (include_handles) { + /* Left handle - only if applicable. + * NOTE: for the very first keyframe, + * the left handle actually has no bearings on anything. */ + if (prev_bezt && (prev_bezt->ipo == BEZT_IPO_BEZ)) { + *r_min = min_ff(*r_min, bezt->vec[0][1]); + *r_max = max_ff(*r_max, bezt->vec[0][1]); + } - foundvert = true; + /* Right handle - only if applicable. */ + if (bezt->ipo == BEZT_IPO_BEZ) { + *r_min = min_ff(*r_min, bezt->vec[2][1]); + *r_max = max_ff(*r_max, bezt->vec[2][1]); } } - else if (fcu->fpt) { - min = min_ff(min, fcu->fpt[0].vec[0]); - max = max_ff(max, fcu->fpt[fcu->totvert - 1].vec[0]); + prev_bezt = bezt; + } +} - foundvert = true; +bool BKE_fcurve_calc_bounds(const FCurve *fcu, + const bool do_sel_only, + const bool include_handles, + const float frame_range[2], + rctf *r_bounds) +{ + if (fcu->totvert == 0) { + return false; + } + + if (fcu->bezt) { + int index_range[2]; + const bool found_indices = get_bounding_bezt_indices( + fcu, do_sel_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, do_sel_only, include_handles, &r_bounds->ymin, &r_bounds->ymax); + } + + else if (fcu->fpt) { + if (frame_range != NULL) { + bool found_indices = false; + for (int i = 0; i < fcu->totvert; i++) { + if (fcu->fpt[i].vec[0] < frame_range[0] || fcu->fpt[i].vec[0] > frame_range[1]) { + continue; + } + if (!found_indices) { + r_bounds->xmin = fcu->fpt[i].vec[0]; + r_bounds->xmax = fcu->fpt[i].vec[0]; + r_bounds->ymin = fcu->fpt[i].vec[1]; + r_bounds->ymax = fcu->fpt[i].vec[1]; + found_indices = true; + } + else { + r_bounds->xmin = min_ff(r_bounds->xmin, fcu->fpt[i].vec[0]); + r_bounds->xmax = max_ff(r_bounds->xmax, fcu->fpt[i].vec[0]); + 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 found_indices; + } + else { + /* X range can be directly calculated from end verts. */ + r_bounds->xmin = fcu->fpt[0].vec[0]; + r_bounds->xmax = fcu->fpt[fcu->totvert - 1].vec[0]; + + r_bounds->ymin = fcu->fpt[0].vec[1]; + r_bounds->ymax = fcu->fpt[0].vec[1]; + for (int i = 0; i < fcu->totvert; 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]); + } } } - if (foundvert == false) { - min = max = 0.0f; + else { + return false; } - if (do_min_length) { - /* Minimum length is 1 frame. */ - if (min == max) { - max += 1.0f; + return true; +} + +bool BKE_fcurve_calc_range(const FCurve *fcu, float *start, float *end, const bool do_sel_only) +{ + float min, max = 0.0f; + bool foundvert = false; + + if (fcu->totvert == 0) { + return false; + } + + if (fcu->bezt) { + int index_range[2]; + foundvert = get_bounding_bezt_indices( + fcu, do_sel_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; diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index 2a761166b27..94ad6a17816 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -3651,14 +3651,8 @@ static void get_normalized_fcurve_bounds(FCurve *fcu, rctf *r_bounds) { const bool fcu_selection_only = false; - BKE_fcurve_calc_bounds(fcu, - &r_bounds->xmin, - &r_bounds->xmax, - &r_bounds->ymin, - &r_bounds->ymax, - fcu_selection_only, - include_handles, - range); + BKE_fcurve_calc_bounds(fcu, fcu_selection_only, include_handles, range, r_bounds); + const short mapping_flag = ANIM_get_normalization_flags(ac); float offset; @@ -3806,6 +3800,7 @@ static int graphkeys_view_selected_channels_exec(bContext *C, wmOperator *op) if (!valid_bounds) { ANIM_animdata_freelist(&anim_data); + WM_report(RPT_WARNING, "No keyframes to focus on."); return OPERATOR_CANCELLED; } @@ -3892,6 +3887,7 @@ static int graphkeys_channel_view_pick_invoke(bContext *C, wmOperator *op, const if (!found_bounds) { ANIM_animdata_freelist(&anim_data); + WM_report(RPT_WARNING, "No keyframes to focus on."); return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index d9772844358..8b15ba42cad 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 f315690a50d..01468d25813 100644 --- a/source/blender/editors/space_graph/graph_draw.c +++ b/source/blender/editors/space_graph/graph_draw.c @@ -633,7 +633,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 e6cbdd63ae6..cd34523d5a6 100644 --- a/source/blender/editors/space_graph/graph_view.c +++ b/source/blender/editors/space_graph/graph_view.c @@ -86,40 +86,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 f92b137d3ae..c534346a607 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -586,7 +586,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) -- 2.30.2 From ac19d19dfc30415b5b1ab66d8646a45aeff13cad Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Fri, 24 Feb 2023 15:40:25 +0100 Subject: [PATCH 02/12] fix issues with values not initialized --- source/blender/blenkernel/intern/fcurve.c | 26 +++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 1789811b99e..e4b5d6a4622 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -629,6 +629,9 @@ static void calculate_bezt_bounds_x(BezTriple *bezt_array, float *r_min, float *r_max) { + *r_min = bezt_array[index_range[0]].vec[1][0]; + *r_max = bezt_array[index_range[0]].vec[1][0]; + 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++) { @@ -653,33 +656,31 @@ static void calculate_bezt_bounds_y(BezTriple *bezt_array, *r_min = bezt_array[index_range[0]].vec[1][1]; *r_max = bezt_array[index_range[0]].vec[1][1]; - BezTriple *bezt, *prev_bezt = NULL; for (int i = index_range[0]; i <= index_range[1]; i++) { - bezt = &bezt_array[i]; + BezTriple bezt = bezt_array[i]; - if (do_sel_only && !BEZT_ISSEL_ANY(bezt)) { + if (do_sel_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]); + *r_min = min_ff(*r_min, bezt.vec[1][1]); + *r_max = max_ff(*r_max, 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 (prev_bezt && (prev_bezt->ipo == BEZT_IPO_BEZ)) { - *r_min = min_ff(*r_min, bezt->vec[0][1]); - *r_max = max_ff(*r_max, bezt->vec[0][1]); + if (i - 1 > 0 && (bezt_array[i - 1].ipo == BEZT_IPO_BEZ)) { + *r_min = min_ff(*r_min, bezt.vec[0][1]); + *r_max = max_ff(*r_max, bezt.vec[0][1]); } /* Right handle - only if applicable. */ - if (bezt->ipo == BEZT_IPO_BEZ) { - *r_min = min_ff(*r_min, bezt->vec[2][1]); - *r_max = max_ff(*r_max, bezt->vec[2][1]); + if (bezt.ipo == BEZT_IPO_BEZ) { + *r_min = min_ff(*r_min, bezt.vec[2][1]); + *r_max = max_ff(*r_max, bezt.vec[2][1]); } } - prev_bezt = bezt; } } @@ -700,7 +701,6 @@ bool BKE_fcurve_calc_bounds(const FCurve *fcu, 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( -- 2.30.2 From 8f1d7045b26510f79220e40d5b628d42ad0fc774 Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Fri, 24 Feb 2023 15:40:32 +0100 Subject: [PATCH 03/12] add tests --- .../blender/blenkernel/intern/fcurve_test.cc | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) diff --git a/source/blender/blenkernel/intern/fcurve_test.cc b/source/blender/blenkernel/intern/fcurve_test.cc index 66c4477ebc2..e3470a22df6 100644 --- a/source/blender/blenkernel/intern/fcurve_test.cc +++ b/source/blender/blenkernel/intern/fcurve_test.cc @@ -346,4 +346,153 @@ 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); + 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); + + fcu->bezt[1].f2 |= SELECT; + fcu->bezt[3].f2 |= SELECT; + + success = BKE_fcurve_calc_range(fcu, &min, &max, true); + EXPECT_TRUE(success); + 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); + + 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); + 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); + + 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); + 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); + 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); + + 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); + 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); + 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); + 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); + + BKE_fcurve_free(fcu); +} + } // namespace blender::bke::tests -- 2.30.2 From a2b16f370c4f5d18cf7360f900659b7be14ce624 Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Fri, 24 Feb 2023 15:56:32 +0100 Subject: [PATCH 04/12] remove warnings shouldn't be part of this patch --- source/blender/editors/animation/anim_channels_edit.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index 94ad6a17816..28851b04207 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -3800,7 +3800,6 @@ static int graphkeys_view_selected_channels_exec(bContext *C, wmOperator *op) if (!valid_bounds) { ANIM_animdata_freelist(&anim_data); - WM_report(RPT_WARNING, "No keyframes to focus on."); return OPERATOR_CANCELLED; } @@ -3887,7 +3886,6 @@ static int graphkeys_channel_view_pick_invoke(bContext *C, wmOperator *op, const if (!found_bounds) { ANIM_animdata_freelist(&anim_data); - WM_report(RPT_WARNING, "No keyframes to focus on."); return OPERATOR_CANCELLED; } -- 2.30.2 From 4fb84b4bf0f98a0c33c95165abd2b1afebc401ba Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Thu, 2 Mar 2023 12:14:41 +0100 Subject: [PATCH 05/12] include sybrens comments --- source/blender/blenkernel/BKE_fcurve.h | 5 +- source/blender/blenkernel/intern/fcurve.c | 182 ++++++++++++---------- 2 files changed, 106 insertions(+), 81 deletions(-) diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index 221114523dc..2f268c2c438 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -367,8 +367,9 @@ int BKE_fcurve_pathcache_find_array(struct FCurvePathCache *fcache, /** * 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 BKE_fcurve_calc_range(const struct FCurve *fcu, float *r_min, float *r_max, bool selected_keys_only); /** * Calculate the x and y extents of F-Curve's data. @@ -377,7 +378,7 @@ bool BKE_fcurve_calc_range(const struct FCurve *fcu, float *min, float *max, boo * \return true if the bounds have been found. */ bool BKE_fcurve_calc_bounds(const struct FCurve *fcu, - bool do_sel_only, + bool selected_keys_only, bool include_handles, const float frame_range[2], struct rctf *r_bounds); diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index e4b5d6a4622..048aef6791c 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -557,11 +557,11 @@ int BKE_fcurve_bezt_binarysearch_index(const BezTriple array[], /* ...................................... */ /* Get the first and last index to the bezt array that satisfies the given parameters. - * \param do_sel_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. + * \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 do_sel_only, + const bool selected_keys_only, const float frame_range[2], int *r_first, int *r_last) @@ -595,7 +595,7 @@ static bool get_bounding_bezt_indices(const FCurve *fcu, } /* Only include selected items? */ - if (do_sel_only) { + if (selected_keys_only) { /* Find first selected. */ for (int i = *r_first; i <= *r_last; i++) { BezTriple *bezt = &fcu->bezt[i]; @@ -630,25 +630,21 @@ static void calculate_bezt_bounds_x(BezTriple *bezt_array, float *r_max) { *r_min = bezt_array[index_range[0]].vec[1][0]; - *r_max = bezt_array[index_range[0]].vec[1][0]; + *r_max = bezt_array[index_range[1]].vec[1][0]; 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++) { - 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]); + 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]); } } - else { - *r_min = bezt_array[index_range[0]].vec[1][0]; - *r_max = bezt_array[index_range[1]].vec[1][0]; - } } static void calculate_bezt_bounds_y(BezTriple *bezt_array, const int index_range[2], - const bool do_sel_only, + const bool selected_keys_only, const bool include_handles, float *r_min, float *r_max) @@ -657,35 +653,103 @@ static void calculate_bezt_bounds_y(BezTriple *bezt_array, *r_max = bezt_array[index_range[0]].vec[1][1]; for (int i = index_range[0]; i <= index_range[1]; i++) { - BezTriple bezt = bezt_array[i]; + const BezTriple *bezt = &bezt_array[i]; - if (do_sel_only && !BEZT_ISSEL_ANY(&bezt)) { + 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]); + *r_min = min_ff(*r_min, bezt->vec[1][1]); + *r_max = max_ff(*r_max, 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 (i - 1 > 0 && (bezt_array[i - 1].ipo == BEZT_IPO_BEZ)) { - *r_min = min_ff(*r_min, bezt.vec[0][1]); - *r_max = max_ff(*r_max, bezt.vec[0][1]); + /* Left handle - only if applicable. */ + if (i == 0 || (i - 1 > 0 && (bezt_array[i - 1].ipo == BEZT_IPO_BEZ))) { + *r_min = min_ff(*r_min, bezt->vec[0][1]); + *r_max = max_ff(*r_max, bezt->vec[0][1]); } /* Right handle - only if applicable. */ if (bezt.ipo == BEZT_IPO_BEZ) { - *r_min = min_ff(*r_min, bezt.vec[2][1]); - *r_max = max_ff(*r_max, bezt.vec[2][1]); + *r_min = min_ff(*r_min, bezt->vec[2][1]); + *r_max = max_ff(*r_max, 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 = FLT_MAX; + r_bounds->xmax = -FLT_MAX; + r_bounds->ymin = FLT_MAX; + r_bounds->ymax = -FLT_MAX; + + if (frame_range != NULL) { + /* Start index can be calculated because fpt has a key on every full frame. */ + const int start_index = int(frame_range[0] - fcu->fpt[0].vec[0]); + const int end_index = start_index + int(frame_range[1] - frame_range[0]); + + if (start_index > fcu->totvert - 1 || end_index < 0) { + /* Range is outside of keyframe samples. */ + return false; + } + + /* Range might be partially covering keyframe samples. */ + const int start_index_clamped = clamp_i(start_index, 0 fcu->totvert - 1); + const int end_index_clamped = clamp_i(end_index, 0, fcu->totvert - 1); + + r_bounds->xmin = fcu->fpt[start_index_clamped].vec[0]; + r_bounds->xmax = fcu->fpt[end_index_clamped].vec[0]; + + for (int i = start_index_clamped; i <= end_index_clamped; 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 r_bounds->xmin <= r_bounds->xmax; + } + + else { + /* X range can be directly calculated from end verts. */ + r_bounds->xmin = fcu->fpt[0].vec[0]; + r_bounds->xmax = fcu->fpt[fcu->totvert - 1].vec[0]; + + r_bounds->ymin = fcu->fpt[0].vec[1]; + r_bounds->ymax = fcu->fpt[0].vec[1]; + for (int i = 0; i < fcu->totvert; 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 true; +} + bool BKE_fcurve_calc_bounds(const FCurve *fcu, - const bool do_sel_only, + const bool selected_keys_only, const bool include_handles, const float frame_range[2], rctf *r_bounds) @@ -695,63 +759,23 @@ bool BKE_fcurve_calc_bounds(const FCurve *fcu, } if (fcu->bezt) { - int index_range[2]; - const bool found_indices = get_bounding_bezt_indices( - fcu, do_sel_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, do_sel_only, include_handles, &r_bounds->ymin, &r_bounds->ymax); + const bool found_bounds = calculate_bezt_bounds( + fcu, selected_keys_only, include_handles, frame_range, r_bounds); + return found_bounds; } else if (fcu->fpt) { - if (frame_range != NULL) { - bool found_indices = false; - for (int i = 0; i < fcu->totvert; i++) { - if (fcu->fpt[i].vec[0] < frame_range[0] || fcu->fpt[i].vec[0] > frame_range[1]) { - continue; - } - if (!found_indices) { - r_bounds->xmin = fcu->fpt[i].vec[0]; - r_bounds->xmax = fcu->fpt[i].vec[0]; - r_bounds->ymin = fcu->fpt[i].vec[1]; - r_bounds->ymax = fcu->fpt[i].vec[1]; - found_indices = true; - } - else { - r_bounds->xmin = min_ff(r_bounds->xmin, fcu->fpt[i].vec[0]); - r_bounds->xmax = max_ff(r_bounds->xmax, fcu->fpt[i].vec[0]); - 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 found_indices; - } - else { - /* X range can be directly calculated from end verts. */ - r_bounds->xmin = fcu->fpt[0].vec[0]; - r_bounds->xmax = fcu->fpt[fcu->totvert - 1].vec[0]; - - r_bounds->ymin = fcu->fpt[0].vec[1]; - r_bounds->ymax = fcu->fpt[0].vec[1]; - for (int i = 0; i < fcu->totvert; 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]); - } - } + const bool founds_bounds = calculate_fpt_bounds(fcu, frame_range, r_bounds); + return founds_bounds; } - else { - return false; - } - - return true; + return false; } -bool BKE_fcurve_calc_range(const FCurve *fcu, float *start, float *end, const bool do_sel_only) +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; @@ -763,7 +787,7 @@ bool BKE_fcurve_calc_range(const FCurve *fcu, float *start, float *end, const bo if (fcu->bezt) { int index_range[2]; foundvert = get_bounding_bezt_indices( - fcu, do_sel_only, NULL, &index_range[0], &index_range[1]); + fcu, selected_keys_only, NULL, &index_range[0], &index_range[1]); if (!foundvert) { return false; } @@ -777,8 +801,8 @@ bool BKE_fcurve_calc_range(const FCurve *fcu, float *start, float *end, const bo foundvert = true; } - *start = min; - *end = max; + *r_start = min; + *r_end = max; return foundvert; } -- 2.30.2 From 40e149e06b859456295862c12dd6d0abba693b37 Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Thu, 2 Mar 2023 12:37:02 +0100 Subject: [PATCH 06/12] implement comments in tests --- source/blender/blenkernel/intern/fcurve.c | 8 +++--- .../blender/blenkernel/intern/fcurve_test.cc | 28 +++++++++++-------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 048aef6791c..2ae5c412b69 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -670,7 +670,7 @@ static void calculate_bezt_bounds_y(BezTriple *bezt_array, } /* Right handle - only if applicable. */ - if (bezt.ipo == BEZT_IPO_BEZ) { + if (bezt->ipo == BEZT_IPO_BEZ) { *r_min = min_ff(*r_min, bezt->vec[2][1]); *r_max = max_ff(*r_max, bezt->vec[2][1]); } @@ -710,8 +710,8 @@ static bool calculate_fpt_bounds(const FCurve *fcu, const float frame_range[2], if (frame_range != NULL) { /* Start index can be calculated because fpt has a key on every full frame. */ - const int start_index = int(frame_range[0] - fcu->fpt[0].vec[0]); - const int end_index = start_index + int(frame_range[1] - frame_range[0]); + const int start_index = (int)(frame_range[0] - fcu->fpt[0].vec[0]); + const int end_index = start_index + (int)(frame_range[1] - frame_range[0]); if (start_index > fcu->totvert - 1 || end_index < 0) { /* Range is outside of keyframe samples. */ @@ -719,7 +719,7 @@ static bool calculate_fpt_bounds(const FCurve *fcu, const float frame_range[2], } /* Range might be partially covering keyframe samples. */ - const int start_index_clamped = clamp_i(start_index, 0 fcu->totvert - 1); + const int start_index_clamped = clamp_i(start_index, 0, fcu->totvert - 1); const int end_index_clamped = clamp_i(end_index, 0, fcu->totvert - 1); r_bounds->xmin = fcu->fpt[start_index_clamped].vec[0]; diff --git a/source/blender/blenkernel/intern/fcurve_test.cc b/source/blender/blenkernel/intern/fcurve_test.cc index e3470a22df6..3ca587064fb 100644 --- a/source/blender/blenkernel/intern/fcurve_test.cc +++ b/source/blender/blenkernel/intern/fcurve_test.cc @@ -367,19 +367,20 @@ TEST(BKE_fcurve, BKE_fcurve_calc_range) /* All keys. */ success = BKE_fcurve_calc_range(fcu, &min, &max, false); - EXPECT_TRUE(success); + 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); + 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); + 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); @@ -389,7 +390,7 @@ TEST(BKE_fcurve, BKE_fcurve_calc_range) fcurve_store_samples(fcu, NULL, sample_start, sample_end, fcurve_samplingcb_evalcurve); success = BKE_fcurve_calc_range(fcu, &min, &max, true); - EXPECT_TRUE(success); + EXPECT_TRUE(success) << "FCurve samples should have a range."; EXPECT_FLOAT_EQ(sample_start, min); EXPECT_FLOAT_EQ(sample_end, max); @@ -422,7 +423,7 @@ TEST(BKE_fcurve, BKE_fcurve_calc_bounds) /* All keys. */ success = BKE_fcurve_calc_bounds( fcu, false /* sel only */, false /* include handles */, NULL /* frame range */, &bounds); - EXPECT_TRUE(success); + 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); @@ -431,14 +432,15 @@ TEST(BKE_fcurve, BKE_fcurve_calc_bounds) /* Only selected. */ success = BKE_fcurve_calc_bounds( fcu, true /* sel only */, false /* include handles */, NULL /* frame range */, &bounds); - EXPECT_FALSE(success); + 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); + 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); @@ -447,7 +449,7 @@ TEST(BKE_fcurve, BKE_fcurve_calc_bounds) /* Including handles. */ success = BKE_fcurve_calc_bounds( fcu, false /* sel only */, true /* include handles */, NULL /* frame range */, &bounds); - EXPECT_TRUE(success); + 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); @@ -460,13 +462,13 @@ TEST(BKE_fcurve, BKE_fcurve_calc_bounds) range[1] = 30; success = BKE_fcurve_calc_bounds( fcu, false /* sel only */, false /* include handles */, range /* frame range */, &bounds); - EXPECT_FALSE(success); + 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); + 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); @@ -475,7 +477,8 @@ TEST(BKE_fcurve, BKE_fcurve_calc_bounds) /* Range and handles. */ success = BKE_fcurve_calc_bounds( fcu, false /* sel only */, true /* include handles */, range /* frame range */, &bounds); - EXPECT_TRUE(success); + 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); @@ -486,7 +489,8 @@ TEST(BKE_fcurve, BKE_fcurve_calc_bounds) range[1] = 18.2f; success = BKE_fcurve_calc_bounds( fcu, true /* sel only */, true /* include handles */, range /* frame range */, &bounds); - EXPECT_TRUE(success); + 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); -- 2.30.2 From 69053a64213c05272ab86c70311d2a1fbe84681b Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Thu, 2 Mar 2023 12:50:10 +0100 Subject: [PATCH 07/12] add tests for fcu->fpt for BKE_fcurve_calc_bounds --- source/blender/blenkernel/intern/fcurve.c | 4 +-- .../blender/blenkernel/intern/fcurve_test.cc | 32 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 2ae5c412b69..a89b48d4755 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -710,8 +710,8 @@ static bool calculate_fpt_bounds(const FCurve *fcu, const float frame_range[2], if (frame_range != NULL) { /* Start index can be calculated because fpt has a key on every full frame. */ - const int start_index = (int)(frame_range[0] - fcu->fpt[0].vec[0]); - const int end_index = start_index + (int)(frame_range[1] - frame_range[0]); + const float start_index = frame_range[0] - fcu->fpt[0].vec[0]; + const float end_index = start_index + frame_range[1] - frame_range[0]; if (start_index > fcu->totvert - 1 || end_index < 0) { /* Range is outside of keyframe samples. */ diff --git a/source/blender/blenkernel/intern/fcurve_test.cc b/source/blender/blenkernel/intern/fcurve_test.cc index 3ca587064fb..e95ef50dd57 100644 --- a/source/blender/blenkernel/intern/fcurve_test.cc +++ b/source/blender/blenkernel/intern/fcurve_test.cc @@ -496,6 +496,38 @@ TEST(BKE_fcurve, BKE_fcurve_calc_bounds) 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); } -- 2.30.2 From 0d40d384595bf639115d6a26452398860ed2e4b0 Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Thu, 2 Mar 2023 13:00:54 +0100 Subject: [PATCH 08/12] remove check for keyframe type when getting bounds with handles --- source/blender/blenkernel/intern/fcurve.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index a89b48d4755..8942d92a7f4 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -663,17 +663,8 @@ static void calculate_bezt_bounds_y(BezTriple *bezt_array, *r_max = max_ff(*r_max, bezt->vec[1][1]); if (include_handles) { - /* Left handle - only if applicable. */ - if (i == 0 || (i - 1 > 0 && (bezt_array[i - 1].ipo == BEZT_IPO_BEZ))) { - *r_min = min_ff(*r_min, bezt->vec[0][1]); - *r_max = max_ff(*r_max, bezt->vec[0][1]); - } - - /* Right handle - only if applicable. */ - if (bezt->ipo == BEZT_IPO_BEZ) { - *r_min = min_ff(*r_min, bezt->vec[2][1]); - *r_max = max_ff(*r_max, bezt->vec[2][1]); - } + *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]); } } } -- 2.30.2 From 63432e312df8315d4bd74246794e1276eca5bcbc Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Fri, 3 Mar 2023 10:42:47 +0100 Subject: [PATCH 09/12] resolve merge conflicts --- source/blender/editors/animation/anim_channels_edit.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index 4bd6caff599..7e47702082a 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -3726,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; } } @@ -3880,6 +3880,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; -- 2.30.2 From 1fbf03eb8a4d3507eb6cbd662d56a5232eba05ca Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Thu, 9 Mar 2023 12:06:38 +0100 Subject: [PATCH 10/12] implement changes to calculate_fpt_bounds --- source/blender/blenkernel/intern/fcurve.c | 48 ++++++++++------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 5d4ebafdb7e..d2a4d2d00e2 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -694,15 +694,20 @@ static bool calculate_bezt_bounds(const FCurve *fcu, static bool calculate_fpt_bounds(const FCurve *fcu, const float frame_range[2], rctf *r_bounds) { - r_bounds->xmin = FLT_MAX; - r_bounds->xmax = -FLT_MAX; - r_bounds->ymin = FLT_MAX; - r_bounds->ymax = -FLT_MAX; + 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 = frame_range[0] - fcu->fpt[0].vec[0]; - const float end_index = start_index + frame_range[1] - frame_range[0]; + const float start_index_f = frame_range[0] - fcu->fpt[0].vec[0]; + const float end_index_f = start_index + frame_range[1] - frame_range[0]; if (start_index > fcu->totvert - 1 || end_index < 0) { /* Range is outside of keyframe samples. */ @@ -710,33 +715,20 @@ static bool calculate_fpt_bounds(const FCurve *fcu, const float frame_range[2], } /* Range might be partially covering keyframe samples. */ - const int start_index_clamped = clamp_i(start_index, 0, fcu->totvert - 1); - const int end_index_clamped = clamp_i(end_index, 0, fcu->totvert - 1); - - r_bounds->xmin = fcu->fpt[start_index_clamped].vec[0]; - r_bounds->xmax = fcu->fpt[end_index_clamped].vec[0]; - - for (int i = start_index_clamped; i <= end_index_clamped; 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 r_bounds->xmin <= r_bounds->xmax; + start_index = clamp_i(start_index, 0, fcu->totvert - 1); + end_index = clamp_i(end_index, 0, fcu->totvert - 1); } - else { - /* X range can be directly calculated from end verts. */ - r_bounds->xmin = fcu->fpt[0].vec[0]; - r_bounds->xmax = fcu->fpt[fcu->totvert - 1].vec[0]; + /* 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]; - r_bounds->ymin = fcu->fpt[0].vec[1]; - r_bounds->ymax = fcu->fpt[0].vec[1]; - for (int i = 0; i < fcu->totvert; 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]); - } + 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 true; + return BLI_rctf_is_valid(r_bounds); } bool BKE_fcurve_calc_bounds(const FCurve *fcu, -- 2.30.2 From 39433707bee3992d1ce93606ef59f62b24c47ee1 Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Thu, 9 Mar 2023 12:20:26 +0100 Subject: [PATCH 11/12] fix issues coming from structure change --- source/blender/blenkernel/intern/fcurve.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index d2a4d2d00e2..65603c2791b 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -707,16 +707,16 @@ static bool calculate_fpt_bounds(const FCurve *fcu, const float frame_range[2], 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 + frame_range[1] - frame_range[0]; + const float end_index_f = start_index_f + frame_range[1] - frame_range[0]; - if (start_index > fcu->totvert - 1 || end_index < 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, 0, fcu->totvert - 1); - end_index = clamp_i(end_index, 0, fcu->totvert - 1); + 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. */ -- 2.30.2 From 183d3528afef38c1c9dd6f156ebff32ea17af04c Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Fri, 10 Mar 2023 11:23:08 +0100 Subject: [PATCH 12/12] remove else --- source/blender/blenkernel/intern/fcurve.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 65603c2791b..3b87d4d996e 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -747,7 +747,7 @@ bool BKE_fcurve_calc_bounds(const FCurve *fcu, return found_bounds; } - else if (fcu->fpt) { + if (fcu->fpt) { const bool founds_bounds = calculate_fpt_bounds(fcu, frame_range, r_bounds); return founds_bounds; } -- 2.30.2