From ee6420503fe5e09bfd3987f3bb94c9cd6748e1b3 Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Fri, 10 Mar 2023 13:58:01 +0100 Subject: [PATCH 01/12] revive old patch --- scripts/startup/bl_ui/space_graph.py | 1 + .../editors/animation/keyframes_general.c | 47 ++++++++ .../editors/include/ED_keyframes_edit.h | 11 ++ .../editors/space_graph/graph_intern.h | 1 + .../blender/editors/space_graph/graph_ops.c | 1 + .../editors/space_graph/graph_slider_ops.c | 106 ++++++++++++++++++ 6 files changed, 167 insertions(+) diff --git a/scripts/startup/bl_ui/space_graph.py b/scripts/startup/bl_ui/space_graph.py index 9f016f0dfc3..069ca3245af 100644 --- a/scripts/startup/bl_ui/space_graph.py +++ b/scripts/startup/bl_ui/space_graph.py @@ -330,6 +330,7 @@ class GRAPH_MT_slider(Menu): layout.operator("graph.blend_to_neighbor", text="Blend to Neighbor") layout.operator("graph.blend_to_default", text="Blend to Default Value") layout.operator("graph.ease", text="Ease") + layout.operator("graph.gauss_smooth", text="Gauss Smooth") class GRAPH_MT_view_pie(Menu): diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index 4997a9fe121..eac33da617a 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -392,7 +392,44 @@ void blend_to_default_fcurve(PointerRNA *id_ptr, FCurve *fcu, const float factor move_key(&fcu->bezt[i], key_y_value); } } +/* ---------------- */ +void get_1d_gauss_kernel(const int filter_width, const float sigma, double *kernel) +{ + double norm = 1.0 / (M_2_SQRTPI * sigma); + double sig_sq = 2.0 * sigma * sigma; + double sum = 0.0; + + for (int i = -filter_width; i <= filter_width; i++) { + kernel[i + filter_width] = norm * exp(-i * i / sig_sq); + sum += kernel[i + filter_width]; + } + + /* Normalize kernel values. */ + for (int i = 0; i < filter_width * 2 + 1; i++) { + kernel[i] = kernel[i] / sum; + } +} + +void smooth_fcurve_segment(FCurve *fcu, + FCurveSegment *segment, + float *samples, + const float factor, + const int filter_order, + double *kernel) +{ + for (int i = segment->start_index; i < segment->start_index + segment->length; i++) { + const int sample_index = (int)(fcu->bezt[i].vec[1][0] - + fcu->bezt[segment->start_index].vec[1][0]) + + filter_order; + double filter_result = 0; + /* Apply the kernel. */ + for (int j = -filter_order; j <= filter_order; j++) { + filter_result += samples[sample_index + j] * kernel[filter_order + j]; + } + fcu->bezt[i].vec[1][1] = interpf((float)filter_result, samples[sample_index], factor); + } +} /* ---------------- */ void ease_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor) @@ -680,6 +717,16 @@ typedef struct TempFrameValCache { float frame, val; } TempFrameValCache; +void sample_fcurve_segment(FCurve *fcu, + const float start_frame, + float *samples, + const int sample_count) +{ + for (int i = 0; i < sample_count; i++) { + samples[i] = evaluate_fcurve(fcu, start_frame + i); + } +} + void sample_fcurve(FCurve *fcu) { BezTriple *bezt, *start = NULL, *end = NULL; diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h index 5bd672072b1..92c735c0ce3 100644 --- a/source/blender/editors/include/ED_keyframes_edit.h +++ b/source/blender/editors/include/ED_keyframes_edit.h @@ -424,6 +424,13 @@ void blend_to_neighbor_fcurve_segment(struct FCurve *fcu, struct FCurveSegment *segment, float factor); void breakdown_fcurve_segment(struct FCurve *fcu, struct FCurveSegment *segment, float factor); +void get_1d_gauss_kernel(int filter_width, const float sigma, double *kernel); +void smooth_fcurve_segment(struct FCurve *fcu, + struct FCurveSegment *segment, + float *samples, + float factor, + int filter_order, + double *kernel); void ease_fcurve_segment(struct FCurve *fcu, struct FCurveSegment *segment, float factor); bool decimate_fcurve(struct bAnimListElem *ale, float remove_ratio, float error_sq_max); void blend_to_default_fcurve(struct PointerRNA *id_ptr, struct FCurve *fcu, float factor); @@ -432,6 +439,10 @@ void blend_to_default_fcurve(struct PointerRNA *id_ptr, struct FCurve *fcu, floa */ void smooth_fcurve(struct FCurve *fcu); void sample_fcurve(struct FCurve *fcu); +void sample_fcurve_segment(struct FCurve *fcu, + float start_frame, + float *r_samples, + int sample_count); /* ----------- */ diff --git a/source/blender/editors/space_graph/graph_intern.h b/source/blender/editors/space_graph/graph_intern.h index a685216db31..0459a83a7f8 100644 --- a/source/blender/editors/space_graph/graph_intern.h +++ b/source/blender/editors/space_graph/graph_intern.h @@ -116,6 +116,7 @@ void GRAPH_OT_breakdown(struct wmOperatorType *ot); void GRAPH_OT_ease(struct wmOperatorType *ot); void GRAPH_OT_decimate(struct wmOperatorType *ot); void GRAPH_OT_blend_to_default(struct wmOperatorType *ot); +void GRAPH_OT_gauss_smooth(struct wmOperatorType *ot); void GRAPH_OT_sample(struct wmOperatorType *ot); void GRAPH_OT_bake(struct wmOperatorType *ot); void GRAPH_OT_unbake(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c index b178a3d3430..fe103f6ed7a 100644 --- a/source/blender/editors/space_graph/graph_ops.c +++ b/source/blender/editors/space_graph/graph_ops.c @@ -464,6 +464,7 @@ void graphedit_operatortypes(void) WM_operatortype_append(GRAPH_OT_breakdown); WM_operatortype_append(GRAPH_OT_ease); WM_operatortype_append(GRAPH_OT_blend_to_default); + WM_operatortype_append(GRAPH_OT_gauss_smooth); WM_operatortype_append(GRAPH_OT_euler_filter); WM_operatortype_append(GRAPH_OT_delete); WM_operatortype_append(GRAPH_OT_duplicate); diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index 1bce22959ee..6eb710e1a25 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -1053,3 +1053,109 @@ void GRAPH_OT_ease(wmOperatorType *ot) } /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Gauss Smooth Operator + * \{ */ + +static void gauss_smooth_graph_keys(bAnimContext *ac, + const float factor, + const float sigma, + const int filter_width) +{ + ListBase anim_data = {NULL, NULL}; + ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype); + + bAnimListElem *ale; + + double *kernel = MEM_callocN(sizeof(double) * (filter_width * 2 + 1), "Gauss Kernel"); + get_1d_gauss_kernel(filter_width, sigma, kernel); + + for (ale = anim_data.first; ale; ale = ale->next) { + FCurve *fcu = (FCurve *)ale->key_data; + ListBase segments = find_fcurve_segments(fcu); + + LISTBASE_FOREACH (FCurveSegment *, segment, &segments) { + BezTriple left_bezt = fcu->bezt[segment->start_index]; + BezTriple right_bezt = fcu->bezt[segment->start_index + segment->length - 1]; + const int sample_count = (int)(right_bezt.vec[1][0] - left_bezt.vec[1][0]) + + (filter_width * 2) + 1; + float *samples = MEM_callocN(sizeof(float) * sample_count, "Smooth FCurve Op Samples"); + sample_fcurve_segment(fcu, left_bezt.vec[1][0] - filter_width, samples, sample_count); + smooth_fcurve_segment(fcu, segment, samples, factor, filter_width, kernel); + MEM_freeN(samples); + } + + BLI_freelistN(&segments); + ale->update |= ANIM_UPDATE_DEFAULT; + } + + MEM_freeN(kernel); + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); +} + +static int gauss_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + + if (ANIM_animdata_get_context(C, &ac) == 0) { + return OPERATOR_CANCELLED; + } + const float factor = RNA_float_get(op->ptr, "factor"); + const float sigma = RNA_float_get(op->ptr, "sigma"); + const int filter_width = RNA_int_get(op->ptr, "filter_width"); + gauss_smooth_graph_keys(&ac, factor, sigma, filter_width); + + /* Set notifier that keyframes have changed. */ + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GRAPH_OT_gauss_smooth(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Gauss Smooth"; + ot->idname = "GRAPH_OT_gauss_smooth"; + ot->description = "Smooth the curve using a Gauss filter"; + + /* API callbacks. */ + /* ot->invoke = fft_invoke; */ + /* ot->modal = fft_modal; */ + ot->exec = gauss_exec; + ot->poll = graphop_editable_keyframes_poll; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_float_factor(ot->srna, + "factor", + 1.0f, + 0.0f, + FLT_MAX, + "Factor", + "How much to blend to the default value", + 0.0f, + 1.0f); + + RNA_def_float(ot->srna, + "sigma", + 2.0f, + 0.001f, + FLT_MAX, + "Sigma", + "At which frquency the factor should be applied", + 0.001f, + 100.0f); + + RNA_def_int(ot->srna, + "filter_width", + 8, + 1, + 64, + "Filter Width", + "How far to each side the operator will average the key values", + 1, + 32); +} +/** \} */ -- 2.30.2 From c0a7deab256ff73b1c6aeef56d1792569e6b99f7 Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Thu, 16 Mar 2023 15:31:27 +0100 Subject: [PATCH 02/12] make operator modal --- .../editors/animation/keyframes_general.c | 30 +++++++---- .../editors/include/ED_keyframes_edit.h | 8 ++- .../editors/space_graph/graph_slider_ops.c | 51 +++++++++++++++---- 3 files changed, 66 insertions(+), 23 deletions(-) diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index eac33da617a..8b15de4fad4 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -394,20 +394,28 @@ void blend_to_default_fcurve(PointerRNA *id_ptr, FCurve *fcu, const float factor } /* ---------------- */ -void get_1d_gauss_kernel(const int filter_width, const float sigma, double *kernel) +void get_1d_gauss_kernel(const float sigma, const int kernel_size, double *r_kernel) { double norm = 1.0 / (M_2_SQRTPI * sigma); - double sig_sq = 2.0 * sigma * sigma; + double sigma_sq = 2.0 * sigma * sigma; double sum = 0.0; - for (int i = -filter_width; i <= filter_width; i++) { - kernel[i + filter_width] = norm * exp(-i * i / sig_sq); - sum += kernel[i + filter_width]; + for (int i = 0; i < kernel_size; i++) { + const double normalized_index = (double)i / (kernel_size - 1); + r_kernel[i] = norm * exp(-normalized_index * normalized_index / sigma_sq); + if (i == 0) { + sum += r_kernel[i]; + } + else { + /* We only calculate half the kernel, + * the normalization needs to take that into account. */ + sum += r_kernel[i] * 2; + } } /* Normalize kernel values. */ - for (int i = 0; i < filter_width * 2 + 1; i++) { - kernel[i] = kernel[i] / sum; + for (int i = 0; i < kernel_size; i++) { + r_kernel[i] = r_kernel[i] / sum; } } @@ -415,17 +423,17 @@ void smooth_fcurve_segment(FCurve *fcu, FCurveSegment *segment, float *samples, const float factor, - const int filter_order, + const int kernel_size, double *kernel) { for (int i = segment->start_index; i < segment->start_index + segment->length; i++) { const int sample_index = (int)(fcu->bezt[i].vec[1][0] - fcu->bezt[segment->start_index].vec[1][0]) + - filter_order; + kernel_size; double filter_result = 0; /* Apply the kernel. */ - for (int j = -filter_order; j <= filter_order; j++) { - filter_result += samples[sample_index + j] * kernel[filter_order + j]; + for (int j = -kernel_size; j <= kernel_size; j++) { + filter_result += samples[sample_index + j] * kernel[abs(j)]; } fcu->bezt[i].vec[1][1] = interpf((float)filter_result, samples[sample_index], factor); } diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h index 92c735c0ce3..1ac48d7880a 100644 --- a/source/blender/editors/include/ED_keyframes_edit.h +++ b/source/blender/editors/include/ED_keyframes_edit.h @@ -424,12 +424,16 @@ void blend_to_neighbor_fcurve_segment(struct FCurve *fcu, struct FCurveSegment *segment, float factor); void breakdown_fcurve_segment(struct FCurve *fcu, struct FCurveSegment *segment, float factor); -void get_1d_gauss_kernel(int filter_width, const float sigma, double *kernel); +/** Get a 1D gauss kernel. Since the kernel is symmetrical, only calculates the positive side. + * \param sigma The shape of the gauss distribution. + * \param kernel_size How long the kernel array is. + */ +void get_1d_gauss_kernel(const float sigma, int kernel_size, double *r_kernel); void smooth_fcurve_segment(struct FCurve *fcu, struct FCurveSegment *segment, float *samples, float factor, - int filter_order, + int kernel_size, double *kernel); void ease_fcurve_segment(struct FCurve *fcu, struct FCurveSegment *segment, float factor); bool decimate_fcurve(struct bAnimListElem *ale, float remove_ratio, float error_sq_max); diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index 6eb710e1a25..ab45ed4825e 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -1065,12 +1065,11 @@ static void gauss_smooth_graph_keys(bAnimContext *ac, ListBase anim_data = {NULL, NULL}; ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype); - bAnimListElem *ale; + const int kernel_size = filter_width + 1; + double *kernel = MEM_callocN(sizeof(double) * kernel_size, "Gauss Kernel"); + get_1d_gauss_kernel(sigma, kernel_size, kernel); - double *kernel = MEM_callocN(sizeof(double) * (filter_width * 2 + 1), "Gauss Kernel"); - get_1d_gauss_kernel(filter_width, sigma, kernel); - - for (ale = anim_data.first; ale; ale = ale->next) { + LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { FCurve *fcu = (FCurve *)ale->key_data; ListBase segments = find_fcurve_segments(fcu); @@ -1078,7 +1077,7 @@ static void gauss_smooth_graph_keys(bAnimContext *ac, BezTriple left_bezt = fcu->bezt[segment->start_index]; BezTriple right_bezt = fcu->bezt[segment->start_index + segment->length - 1]; const int sample_count = (int)(right_bezt.vec[1][0] - left_bezt.vec[1][0]) + - (filter_width * 2) + 1; + (filter_width * 2 + 1); float *samples = MEM_callocN(sizeof(float) * sample_count, "Smooth FCurve Op Samples"); sample_fcurve_segment(fcu, left_bezt.vec[1][0] - filter_width, samples, sample_count); smooth_fcurve_segment(fcu, segment, samples, factor, filter_width, kernel); @@ -1094,6 +1093,38 @@ static void gauss_smooth_graph_keys(bAnimContext *ac, ANIM_animdata_freelist(&anim_data); } +static void gauss_smooth_modal_update(bContext *C, wmOperator *op) +{ + tGraphSliderOp *gso = op->customdata; + + ease_draw_status_header(C, gso); + + /* Reset keyframes to the state at invoke. */ + reset_bezts(gso); + const float factor = slider_factor_get_and_remember(op); + const float sigma = RNA_float_get(op->ptr, "sigma"); + const int filter_width = RNA_int_get(op->ptr, "filter_width"); + gauss_smooth_graph_keys(&gso->ac, factor, sigma, filter_width); + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); +} + +static int gauss_smooth_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + const int invoke_result = graph_slider_invoke(C, op, event); + + if (invoke_result == OPERATOR_CANCELLED) { + return invoke_result; + } + + tGraphSliderOp *gso = op->customdata; + gso->modal_update = gauss_smooth_modal_update; + gso->factor_prop = RNA_struct_find_property(op->ptr, "factor"); + ED_slider_allow_overshoot_set(gso->slider, false); + ease_draw_status_header(C, gso); + + return invoke_result; +} + static int gauss_exec(bContext *C, wmOperator *op) { bAnimContext ac; @@ -1120,8 +1151,8 @@ void GRAPH_OT_gauss_smooth(wmOperatorType *ot) ot->description = "Smooth the curve using a Gauss filter"; /* API callbacks. */ - /* ot->invoke = fft_invoke; */ - /* ot->modal = fft_modal; */ + ot->invoke = gauss_smooth_invoke; + ot->modal = graph_slider_modal; ot->exec = gauss_exec; ot->poll = graphop_editable_keyframes_poll; @@ -1140,7 +1171,7 @@ void GRAPH_OT_gauss_smooth(wmOperatorType *ot) RNA_def_float(ot->srna, "sigma", - 2.0f, + 0.33f, 0.001f, FLT_MAX, "Sigma", @@ -1150,7 +1181,7 @@ void GRAPH_OT_gauss_smooth(wmOperatorType *ot) RNA_def_int(ot->srna, "filter_width", - 8, + 6, 1, 64, "Filter Width", -- 2.30.2 From abb1d5f4bfe50653c40c9c94e4c965cf74e58c12 Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Thu, 16 Mar 2023 16:41:59 +0100 Subject: [PATCH 03/12] store kernel in custom data --- .../editors/space_graph/graph_slider_ops.c | 76 ++++++++++++++++--- 1 file changed, 65 insertions(+), 11 deletions(-) diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index ab45ed4825e..50cb119dd5e 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -69,6 +69,10 @@ typedef struct tGraphSliderOp { /* Each operator has a specific update function. */ void (*modal_update)(struct bContext *, struct wmOperator *); + /* If an operator stores custom data, it also needs to provide the function to clean it up. */ + void *operator_data; + void (*cleanup_operator_data)(void *operator_data); + NumInput num; } tGraphSliderOp; @@ -191,6 +195,10 @@ static void graph_slider_exit(bContext *C, wmOperator *op) return; } + if (gso->operator_data) { + gso->cleanup_operator_data(gso->operator_data); + } + ScrArea *area = gso->area; LinkData *link; @@ -1057,18 +1065,25 @@ void GRAPH_OT_ease(wmOperatorType *ot) /** \name Gauss Smooth Operator * \{ */ +typedef struct tGaussOperatorData { + double *kernel; +} tGaussOperatorData; + +static void gauss_smooth_cleanup_custom_data(void *operator_data) +{ + tGaussOperatorData *gauss_data = (tGaussOperatorData *)operator_data; + MEM_freeN(gauss_data->kernel); + MEM_freeN(gauss_data); +} + static void gauss_smooth_graph_keys(bAnimContext *ac, const float factor, - const float sigma, + double *kernel, const int filter_width) { ListBase anim_data = {NULL, NULL}; ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype); - const int kernel_size = filter_width + 1; - double *kernel = MEM_callocN(sizeof(double) * kernel_size, "Gauss Kernel"); - get_1d_gauss_kernel(sigma, kernel_size, kernel); - LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { FCurve *fcu = (FCurve *)ale->key_data; ListBase segments = find_fcurve_segments(fcu); @@ -1088,23 +1103,46 @@ static void gauss_smooth_graph_keys(bAnimContext *ac, ale->update |= ANIM_UPDATE_DEFAULT; } - MEM_freeN(kernel); ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); } +static void gauss_smooth_draw_status_header(bContext *C, tGraphSliderOp *gso) +{ + char status_str[UI_MAX_DRAW_STR]; + char mode_str[32]; + char slider_string[UI_MAX_DRAW_STR]; + + ED_slider_status_string_get(gso->slider, slider_string, UI_MAX_DRAW_STR); + + strcpy(mode_str, TIP_("Gauss Smooth")); + + if (hasNumInput(&gso->num)) { + char str_ofs[NUM_STR_REP_LEN]; + + outputNumInput(&gso->num, str_ofs, &gso->scene->unit); + + BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, str_ofs); + } + else { + BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, slider_string); + } + + ED_workspace_status_text(C, status_str); +} + static void gauss_smooth_modal_update(bContext *C, wmOperator *op) { tGraphSliderOp *gso = op->customdata; - ease_draw_status_header(C, gso); + gauss_smooth_draw_status_header(C, gso); /* Reset keyframes to the state at invoke. */ reset_bezts(gso); const float factor = slider_factor_get_and_remember(op); - const float sigma = RNA_float_get(op->ptr, "sigma"); + tGaussOperatorData *operator_data = (tGaussOperatorData *)gso->operator_data; const int filter_width = RNA_int_get(op->ptr, "filter_width"); - gauss_smooth_graph_keys(&gso->ac, factor, sigma, filter_width); + gauss_smooth_graph_keys(&gso->ac, factor, operator_data->kernel, filter_width); WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); } @@ -1119,6 +1157,19 @@ static int gauss_smooth_invoke(bContext *C, wmOperator *op, const wmEvent *event tGraphSliderOp *gso = op->customdata; gso->modal_update = gauss_smooth_modal_update; gso->factor_prop = RNA_struct_find_property(op->ptr, "factor"); + + const float sigma = RNA_float_get(op->ptr, "sigma"); + const int filter_width = RNA_int_get(op->ptr, "filter_width"); + const int kernel_size = filter_width + 1; + double *kernel = MEM_callocN(sizeof(double) * kernel_size, "Gauss Kernel"); + get_1d_gauss_kernel(sigma, kernel_size, kernel); + + tGaussOperatorData *operator_data = MEM_callocN(sizeof(tGaussOperatorData), + "tGaussOperatorData"); + operator_data->kernel = kernel; + gso->operator_data = operator_data; + gso->cleanup_operator_data = gauss_smooth_cleanup_custom_data; + ED_slider_allow_overshoot_set(gso->slider, false); ease_draw_status_header(C, gso); @@ -1133,9 +1184,12 @@ static int gauss_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } const float factor = RNA_float_get(op->ptr, "factor"); - const float sigma = RNA_float_get(op->ptr, "sigma"); const int filter_width = RNA_int_get(op->ptr, "filter_width"); - gauss_smooth_graph_keys(&ac, factor, sigma, filter_width); + const int kernel_size = filter_width + 1; + double *kernel = MEM_callocN(sizeof(double) * kernel_size, "Gauss Kernel"); + get_1d_gauss_kernel(RNA_float_get(op->ptr, "sigma"), kernel_size, kernel); + gauss_smooth_graph_keys(&ac, factor, kernel, filter_width); + MEM_freeN(kernel); /* Set notifier that keyframes have changed. */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); -- 2.30.2 From 93e2822984ba4a7d323794d61f25bb7652d09abb Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Fri, 17 Mar 2023 09:10:59 +0100 Subject: [PATCH 04/12] store fcurve samples wip --- .../editors/space_graph/graph_slider_ops.c | 48 ++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index 50cb119dd5e..bdd4905538a 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -1067,12 +1067,23 @@ void GRAPH_OT_ease(wmOperatorType *ot) typedef struct tGaussOperatorData { double *kernel; + ListBase segments /* tFCurveSegmentLink */; } tGaussOperatorData; +typedef struct tFCurveSegmentLink { + tFCurveSegmentLink *prev, *next; + FCurve *fcu; + FCurveSegment *segment; + float *samples; +} tFCurveSegmentLink; + static void gauss_smooth_cleanup_custom_data(void *operator_data) { tGaussOperatorData *gauss_data = (tGaussOperatorData *)operator_data; MEM_freeN(gauss_data->kernel); + LISTBASE_FOREACH (tFCurveSegmentLink *, segment, &gauss_data->segments) { + MEM_freeN(segment->samples); + } MEM_freeN(gauss_data); } @@ -1142,7 +1153,16 @@ static void gauss_smooth_modal_update(bContext *C, wmOperator *op) const float factor = slider_factor_get_and_remember(op); tGaussOperatorData *operator_data = (tGaussOperatorData *)gso->operator_data; const int filter_width = RNA_int_get(op->ptr, "filter_width"); - gauss_smooth_graph_keys(&gso->ac, factor, operator_data->kernel, filter_width); + + LISTBASE_FOREACH (tFCurveSegmentLink *, segment, &operator_data->segments) { + smooth_fcurve_segment(segment->fcu, + segment->segment, + segment->samples, + factor, + filter_width, + operator_data->kernel); + } + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); } @@ -1167,6 +1187,32 @@ static int gauss_smooth_invoke(bContext *C, wmOperator *op, const wmEvent *event tGaussOperatorData *operator_data = MEM_callocN(sizeof(tGaussOperatorData), "tGaussOperatorData"); operator_data->kernel = kernel; + + ListBase anim_data = {NULL, NULL}; + ANIM_animdata_filter( + &gso->ac, &anim_data, OPERATOR_DATA_FILTER, &gso->ac.data, gso->ac.datatype); + + ListBase segments = {NULL, NULL}; + LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { + FCurve *fcu = (FCurve *)ale->key_data; + ListBase segments = find_fcurve_segments(fcu); + LISTBASE_FOREACH (FCurveSegment *, segment, &segments) { + tFCurveSegmentLink *segment_link = MEM_callocN(sizeof(tFCurveSegmentLink), + "FCurve Segment Link"); + segment_link->fcu = fcu; + segment_link->segment = segment; + BezTriple left_bezt = fcu->bezt[segment->start_index]; + BezTriple right_bezt = fcu->bezt[segment->start_index + segment->length - 1]; + const int sample_count = (int)(right_bezt.vec[1][0] - left_bezt.vec[1][0]) + + (filter_width * 2 + 1); + float *samples = MEM_callocN(sizeof(float) * sample_count, "Smooth FCurve Op Samples"); + sample_fcurve_segment(fcu, left_bezt.vec[1][0] - filter_width, samples, sample_count); + segment_link->samples = samples; + BLI_addtail(&segments, segment_link); + } + } + + operator_data->segments = segments; gso->operator_data = operator_data; gso->cleanup_operator_data = gauss_smooth_cleanup_custom_data; -- 2.30.2 From 437605ab2df103e5013178a2e33e1c1333f7ab95 Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Fri, 17 Mar 2023 11:19:26 +0100 Subject: [PATCH 05/12] store fcurve sample data for modal operation --- .../editors/space_graph/graph_slider_ops.c | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index bdd4905538a..06a8902e262 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -1067,11 +1067,11 @@ void GRAPH_OT_ease(wmOperatorType *ot) typedef struct tGaussOperatorData { double *kernel; - ListBase segments /* tFCurveSegmentLink */; + ListBase segment_links /* tFCurveSegmentLink */; } tGaussOperatorData; typedef struct tFCurveSegmentLink { - tFCurveSegmentLink *prev, *next; + struct tFCurveSegmentLink *prev, *next; FCurve *fcu; FCurveSegment *segment; float *samples; @@ -1080,10 +1080,12 @@ typedef struct tFCurveSegmentLink { static void gauss_smooth_cleanup_custom_data(void *operator_data) { tGaussOperatorData *gauss_data = (tGaussOperatorData *)operator_data; - MEM_freeN(gauss_data->kernel); - LISTBASE_FOREACH (tFCurveSegmentLink *, segment, &gauss_data->segments) { - MEM_freeN(segment->samples); + LISTBASE_FOREACH (tFCurveSegmentLink *, segment_link, &gauss_data->segment_links) { + MEM_freeN(segment_link->samples); + MEM_freeN(segment_link->segment); } + MEM_freeN(gauss_data->kernel); + BLI_freelistN(&gauss_data->segment_links); MEM_freeN(gauss_data); } @@ -1148,13 +1150,11 @@ static void gauss_smooth_modal_update(bContext *C, wmOperator *op) gauss_smooth_draw_status_header(C, gso); - /* Reset keyframes to the state at invoke. */ - reset_bezts(gso); const float factor = slider_factor_get_and_remember(op); tGaussOperatorData *operator_data = (tGaussOperatorData *)gso->operator_data; const int filter_width = RNA_int_get(op->ptr, "filter_width"); - LISTBASE_FOREACH (tFCurveSegmentLink *, segment, &operator_data->segments) { + LISTBASE_FOREACH (tFCurveSegmentLink *, segment, &operator_data->segment_links) { smooth_fcurve_segment(segment->fcu, segment->segment, segment->samples, @@ -1189,14 +1189,13 @@ static int gauss_smooth_invoke(bContext *C, wmOperator *op, const wmEvent *event operator_data->kernel = kernel; ListBase anim_data = {NULL, NULL}; - ANIM_animdata_filter( - &gso->ac, &anim_data, OPERATOR_DATA_FILTER, &gso->ac.data, gso->ac.datatype); + ANIM_animdata_filter(&gso->ac, &anim_data, OPERATOR_DATA_FILTER, gso->ac.data, gso->ac.datatype); - ListBase segments = {NULL, NULL}; + ListBase segment_links = {NULL, NULL}; LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { FCurve *fcu = (FCurve *)ale->key_data; - ListBase segments = find_fcurve_segments(fcu); - LISTBASE_FOREACH (FCurveSegment *, segment, &segments) { + ListBase fcu_segments = find_fcurve_segments(fcu); + LISTBASE_FOREACH (FCurveSegment *, segment, &fcu_segments) { tFCurveSegmentLink *segment_link = MEM_callocN(sizeof(tFCurveSegmentLink), "FCurve Segment Link"); segment_link->fcu = fcu; @@ -1208,16 +1207,17 @@ static int gauss_smooth_invoke(bContext *C, wmOperator *op, const wmEvent *event float *samples = MEM_callocN(sizeof(float) * sample_count, "Smooth FCurve Op Samples"); sample_fcurve_segment(fcu, left_bezt.vec[1][0] - filter_width, samples, sample_count); segment_link->samples = samples; - BLI_addtail(&segments, segment_link); + BLI_addtail(&segment_links, segment_link); } } - operator_data->segments = segments; + operator_data->segment_links = segment_links; gso->operator_data = operator_data; gso->cleanup_operator_data = gauss_smooth_cleanup_custom_data; ED_slider_allow_overshoot_set(gso->slider, false); ease_draw_status_header(C, gso); + ANIM_animdata_freelist(&anim_data); return invoke_result; } -- 2.30.2 From f1a2c21ae179497d93c49b68c9fa0768a2aded07 Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Fri, 17 Mar 2023 11:31:26 +0100 Subject: [PATCH 06/12] use move_key function --- source/blender/editors/animation/keyframes_general.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index 8b15de4fad4..4ae12568cf1 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -435,7 +435,8 @@ void smooth_fcurve_segment(FCurve *fcu, for (int j = -kernel_size; j <= kernel_size; j++) { filter_result += samples[sample_index + j] * kernel[abs(j)]; } - fcu->bezt[i].vec[1][1] = interpf((float)filter_result, samples[sample_index], factor); + const float key_y_value = interpf((float)filter_result, samples[sample_index], factor); + move_key(&fcu->bezt[i], key_y_value); } } /* ---------------- */ -- 2.30.2 From 129f2e3874f33b6ac4615762d7f2fb5361a94dcb Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Fri, 17 Mar 2023 11:31:43 +0100 Subject: [PATCH 07/12] extract function gauss_smooth_build_custom_data --- .../editors/space_graph/graph_slider_ops.c | 77 ++++++++++--------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index 06a8902e262..7d48744df58 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -1077,7 +1077,46 @@ typedef struct tFCurveSegmentLink { float *samples; } tFCurveSegmentLink; -static void gauss_smooth_cleanup_custom_data(void *operator_data) +static void gauss_smooth_build_custom_data(tGraphSliderOp *gso, + const int filter_width, + const float sigma) +{ + tGaussOperatorData *operator_data = MEM_callocN(sizeof(tGaussOperatorData), + "tGaussOperatorData"); + const int kernel_size = filter_width + 1; + double *kernel = MEM_callocN(sizeof(double) * kernel_size, "Gauss Kernel"); + get_1d_gauss_kernel(sigma, kernel_size, kernel); + operator_data->kernel = kernel; + + ListBase anim_data = {NULL, NULL}; + ANIM_animdata_filter(&gso->ac, &anim_data, OPERATOR_DATA_FILTER, gso->ac.data, gso->ac.datatype); + + ListBase segment_links = {NULL, NULL}; + LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { + FCurve *fcu = (FCurve *)ale->key_data; + ListBase fcu_segments = find_fcurve_segments(fcu); + LISTBASE_FOREACH (FCurveSegment *, segment, &fcu_segments) { + tFCurveSegmentLink *segment_link = MEM_callocN(sizeof(tFCurveSegmentLink), + "FCurve Segment Link"); + segment_link->fcu = fcu; + segment_link->segment = segment; + BezTriple left_bezt = fcu->bezt[segment->start_index]; + BezTriple right_bezt = fcu->bezt[segment->start_index + segment->length - 1]; + const int sample_count = (int)(right_bezt.vec[1][0] - left_bezt.vec[1][0]) + + (filter_width * 2 + 1); + float *samples = MEM_callocN(sizeof(float) * sample_count, "Smooth FCurve Op Samples"); + sample_fcurve_segment(fcu, left_bezt.vec[1][0] - filter_width, samples, sample_count); + segment_link->samples = samples; + BLI_addtail(&segment_links, segment_link); + } + } + ANIM_animdata_freelist(&anim_data); + + operator_data->segment_links = segment_links; + gso->operator_data = operator_data; +} + +static void gauss_smooth_free_custom_data(void *operator_data) { tGaussOperatorData *gauss_data = (tGaussOperatorData *)operator_data; LISTBASE_FOREACH (tFCurveSegmentLink *, segment_link, &gauss_data->segment_links) { @@ -1180,44 +1219,12 @@ static int gauss_smooth_invoke(bContext *C, wmOperator *op, const wmEvent *event const float sigma = RNA_float_get(op->ptr, "sigma"); const int filter_width = RNA_int_get(op->ptr, "filter_width"); - const int kernel_size = filter_width + 1; - double *kernel = MEM_callocN(sizeof(double) * kernel_size, "Gauss Kernel"); - get_1d_gauss_kernel(sigma, kernel_size, kernel); - tGaussOperatorData *operator_data = MEM_callocN(sizeof(tGaussOperatorData), - "tGaussOperatorData"); - operator_data->kernel = kernel; - - ListBase anim_data = {NULL, NULL}; - ANIM_animdata_filter(&gso->ac, &anim_data, OPERATOR_DATA_FILTER, gso->ac.data, gso->ac.datatype); - - ListBase segment_links = {NULL, NULL}; - LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { - FCurve *fcu = (FCurve *)ale->key_data; - ListBase fcu_segments = find_fcurve_segments(fcu); - LISTBASE_FOREACH (FCurveSegment *, segment, &fcu_segments) { - tFCurveSegmentLink *segment_link = MEM_callocN(sizeof(tFCurveSegmentLink), - "FCurve Segment Link"); - segment_link->fcu = fcu; - segment_link->segment = segment; - BezTriple left_bezt = fcu->bezt[segment->start_index]; - BezTriple right_bezt = fcu->bezt[segment->start_index + segment->length - 1]; - const int sample_count = (int)(right_bezt.vec[1][0] - left_bezt.vec[1][0]) + - (filter_width * 2 + 1); - float *samples = MEM_callocN(sizeof(float) * sample_count, "Smooth FCurve Op Samples"); - sample_fcurve_segment(fcu, left_bezt.vec[1][0] - filter_width, samples, sample_count); - segment_link->samples = samples; - BLI_addtail(&segment_links, segment_link); - } - } - - operator_data->segment_links = segment_links; - gso->operator_data = operator_data; - gso->cleanup_operator_data = gauss_smooth_cleanup_custom_data; + gauss_smooth_build_custom_data(gso, filter_width, sigma); + gso->cleanup_operator_data = gauss_smooth_free_custom_data; ED_slider_allow_overshoot_set(gso->slider, false); ease_draw_status_header(C, gso); - ANIM_animdata_freelist(&anim_data); return invoke_result; } -- 2.30.2 From ef46aa7f7aece9c74cf948aec008443e8ffadbd0 Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Fri, 17 Mar 2023 11:41:27 +0100 Subject: [PATCH 08/12] renames --- .../editors/space_graph/graph_slider_ops.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index 7d48744df58..d93fa2e57ce 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -71,7 +71,7 @@ typedef struct tGraphSliderOp { /* If an operator stores custom data, it also needs to provide the function to clean it up. */ void *operator_data; - void (*cleanup_operator_data)(void *operator_data); + void (*free_operator_data)(void *operator_data); NumInput num; } tGraphSliderOp; @@ -196,7 +196,7 @@ static void graph_slider_exit(bContext *C, wmOperator *op) } if (gso->operator_data) { - gso->cleanup_operator_data(gso->operator_data); + gso->free_operator_data(gso->operator_data); } ScrArea *area = gso->area; @@ -1077,9 +1077,9 @@ typedef struct tFCurveSegmentLink { float *samples; } tFCurveSegmentLink; -static void gauss_smooth_build_custom_data(tGraphSliderOp *gso, - const int filter_width, - const float sigma) +static void gauss_smooth_allocate_operator_data(tGraphSliderOp *gso, + const int filter_width, + const float sigma) { tGaussOperatorData *operator_data = MEM_callocN(sizeof(tGaussOperatorData), "tGaussOperatorData"); @@ -1116,7 +1116,7 @@ static void gauss_smooth_build_custom_data(tGraphSliderOp *gso, gso->operator_data = operator_data; } -static void gauss_smooth_free_custom_data(void *operator_data) +static void gauss_smooth_free_operator_data(void *operator_data) { tGaussOperatorData *gauss_data = (tGaussOperatorData *)operator_data; LISTBASE_FOREACH (tFCurveSegmentLink *, segment_link, &gauss_data->segment_links) { @@ -1220,8 +1220,8 @@ static int gauss_smooth_invoke(bContext *C, wmOperator *op, const wmEvent *event const float sigma = RNA_float_get(op->ptr, "sigma"); const int filter_width = RNA_int_get(op->ptr, "filter_width"); - gauss_smooth_build_custom_data(gso, filter_width, sigma); - gso->cleanup_operator_data = gauss_smooth_free_custom_data; + gauss_smooth_allocate_operator_data(gso, filter_width, sigma); + gso->free_operator_data = gauss_smooth_free_operator_data; ED_slider_allow_overshoot_set(gso->slider, false); ease_draw_status_header(C, gso); @@ -1282,7 +1282,7 @@ void GRAPH_OT_gauss_smooth(wmOperatorType *ot) 0.001f, FLT_MAX, "Sigma", - "At which frquency the factor should be applied", + "The shape of the gauss distribution, lower values make it sharper", 0.001f, 100.0f); -- 2.30.2 From 012e5b14a21386f7d6458a98ddd4c36111427841 Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Fri, 17 Mar 2023 11:56:29 +0100 Subject: [PATCH 09/12] store anim data in operator data --- .../editors/space_graph/graph_slider_ops.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index d93fa2e57ce..ce9419345e8 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -1067,7 +1067,8 @@ void GRAPH_OT_ease(wmOperatorType *ot) typedef struct tGaussOperatorData { double *kernel; - ListBase segment_links /* tFCurveSegmentLink */; + ListBase segment_links; /* tFCurveSegmentLink */ + ListBase anim_data; /* bAnimListElem */ } tGaussOperatorData; typedef struct tFCurveSegmentLink { @@ -1110,8 +1111,8 @@ static void gauss_smooth_allocate_operator_data(tGraphSliderOp *gso, BLI_addtail(&segment_links, segment_link); } } - ANIM_animdata_freelist(&anim_data); + operator_data->anim_data = anim_data; operator_data->segment_links = segment_links; gso->operator_data = operator_data; } @@ -1125,6 +1126,7 @@ static void gauss_smooth_free_operator_data(void *operator_data) } MEM_freeN(gauss_data->kernel); BLI_freelistN(&gauss_data->segment_links); + ANIM_animdata_freelist(&gauss_data->anim_data); MEM_freeN(gauss_data); } @@ -1187,6 +1189,12 @@ static void gauss_smooth_modal_update(bContext *C, wmOperator *op) { tGraphSliderOp *gso = op->customdata; + bAnimContext ac; + + if (ANIM_animdata_get_context(C, &ac) == 0) { + return; + } + gauss_smooth_draw_status_header(C, gso); const float factor = slider_factor_get_and_remember(op); @@ -1202,6 +1210,11 @@ static void gauss_smooth_modal_update(bContext *C, wmOperator *op) operator_data->kernel); } + LISTBASE_FOREACH (bAnimListElem *, ale, &operator_data->anim_data) { + ale->update |= ANIM_UPDATE_DEFAULT; + } + + ANIM_animdata_update(&ac, &operator_data->anim_data); WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); } @@ -1224,7 +1237,7 @@ static int gauss_smooth_invoke(bContext *C, wmOperator *op, const wmEvent *event gso->free_operator_data = gauss_smooth_free_operator_data; ED_slider_allow_overshoot_set(gso->slider, false); - ease_draw_status_header(C, gso); + gauss_smooth_draw_status_header(C, gso); return invoke_result; } -- 2.30.2 From 86ea7b5cc20e40841c2026899a41da670da6f4ea Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Fri, 17 Mar 2023 12:08:30 +0100 Subject: [PATCH 10/12] move code --- .../editors/space_graph/graph_slider_ops.c | 66 ++++++++++--------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index ce9419345e8..91ab34255d6 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -1065,6 +1065,8 @@ void GRAPH_OT_ease(wmOperatorType *ot) /** \name Gauss Smooth Operator * \{ */ +/* It is necessary to store data for smoothing when running in modal, because the sampling of + * FCurves shouldn't be done on every update. */ typedef struct tGaussOperatorData { double *kernel; ListBase segment_links; /* tFCurveSegmentLink */ @@ -1130,37 +1132,6 @@ static void gauss_smooth_free_operator_data(void *operator_data) MEM_freeN(gauss_data); } -static void gauss_smooth_graph_keys(bAnimContext *ac, - const float factor, - double *kernel, - const int filter_width) -{ - ListBase anim_data = {NULL, NULL}; - ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype); - - LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { - FCurve *fcu = (FCurve *)ale->key_data; - ListBase segments = find_fcurve_segments(fcu); - - LISTBASE_FOREACH (FCurveSegment *, segment, &segments) { - BezTriple left_bezt = fcu->bezt[segment->start_index]; - BezTriple right_bezt = fcu->bezt[segment->start_index + segment->length - 1]; - const int sample_count = (int)(right_bezt.vec[1][0] - left_bezt.vec[1][0]) + - (filter_width * 2 + 1); - float *samples = MEM_callocN(sizeof(float) * sample_count, "Smooth FCurve Op Samples"); - sample_fcurve_segment(fcu, left_bezt.vec[1][0] - filter_width, samples, sample_count); - smooth_fcurve_segment(fcu, segment, samples, factor, filter_width, kernel); - MEM_freeN(samples); - } - - BLI_freelistN(&segments); - ale->update |= ANIM_UPDATE_DEFAULT; - } - - ANIM_animdata_update(ac, &anim_data); - ANIM_animdata_freelist(&anim_data); -} - static void gauss_smooth_draw_status_header(bContext *C, tGraphSliderOp *gso) { char status_str[UI_MAX_DRAW_STR]; @@ -1242,6 +1213,37 @@ static int gauss_smooth_invoke(bContext *C, wmOperator *op, const wmEvent *event return invoke_result; } +static void gauss_smooth_graph_keys(bAnimContext *ac, + const float factor, + double *kernel, + const int filter_width) +{ + ListBase anim_data = {NULL, NULL}; + ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype); + + LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { + FCurve *fcu = (FCurve *)ale->key_data; + ListBase segments = find_fcurve_segments(fcu); + + LISTBASE_FOREACH (FCurveSegment *, segment, &segments) { + BezTriple left_bezt = fcu->bezt[segment->start_index]; + BezTriple right_bezt = fcu->bezt[segment->start_index + segment->length - 1]; + const int sample_count = (int)(right_bezt.vec[1][0] - left_bezt.vec[1][0]) + + (filter_width * 2 + 1); + float *samples = MEM_callocN(sizeof(float) * sample_count, "Smooth FCurve Op Samples"); + sample_fcurve_segment(fcu, left_bezt.vec[1][0] - filter_width, samples, sample_count); + smooth_fcurve_segment(fcu, segment, samples, factor, filter_width, kernel); + MEM_freeN(samples); + } + + BLI_freelistN(&segments); + ale->update |= ANIM_UPDATE_DEFAULT; + } + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); +} + static int gauss_exec(bContext *C, wmOperator *op) { bAnimContext ac; @@ -1254,7 +1256,9 @@ static int gauss_exec(bContext *C, wmOperator *op) const int kernel_size = filter_width + 1; double *kernel = MEM_callocN(sizeof(double) * kernel_size, "Gauss Kernel"); get_1d_gauss_kernel(RNA_float_get(op->ptr, "sigma"), kernel_size, kernel); + gauss_smooth_graph_keys(&ac, factor, kernel, filter_width); + MEM_freeN(kernel); /* Set notifier that keyframes have changed. */ -- 2.30.2 From c17a86faf4bf88791ec1e01d7eb66f279e46c7ad Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Thu, 23 Mar 2023 15:13:47 +0100 Subject: [PATCH 11/12] implement sybrens feedback --- scripts/startup/bl_ui/space_graph.py | 2 +- .../editors/animation/keyframes_general.c | 27 +++++----- .../editors/include/ED_keyframes_edit.h | 2 +- .../editors/space_graph/graph_intern.h | 2 +- .../blender/editors/space_graph/graph_ops.c | 2 +- .../editors/space_graph/graph_slider_ops.c | 54 ++++++++++--------- 6 files changed, 47 insertions(+), 42 deletions(-) diff --git a/scripts/startup/bl_ui/space_graph.py b/scripts/startup/bl_ui/space_graph.py index 5a17532af68..999a9e3cef7 100644 --- a/scripts/startup/bl_ui/space_graph.py +++ b/scripts/startup/bl_ui/space_graph.py @@ -330,7 +330,7 @@ class GRAPH_MT_slider(Menu): layout.operator("graph.blend_to_neighbor", text="Blend to Neighbor") layout.operator("graph.blend_to_default", text="Blend to Default Value") layout.operator("graph.ease", text="Ease") - layout.operator("graph.gauss_smooth", text="Gauss Smooth") + layout.operator("graph.gaussian_smooth", text="Smooth") class GRAPH_MT_view_pie(Menu): diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index 4ae12568cf1..bd33782aa17 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -394,15 +394,16 @@ void blend_to_default_fcurve(PointerRNA *id_ptr, FCurve *fcu, const float factor } /* ---------------- */ -void get_1d_gauss_kernel(const float sigma, const int kernel_size, double *r_kernel) +void ED_ANIM_get_1d_gauss_kernel(const float sigma, const int kernel_size, double *r_kernel) { - double norm = 1.0 / (M_2_SQRTPI * sigma); - double sigma_sq = 2.0 * sigma * sigma; + BLI_assert(sigma > 0.0f); + BLI_assert(kernel_size > 0); + const double sigma_sq = 2.0 * sigma * sigma; double sum = 0.0; for (int i = 0; i < kernel_size; i++) { const double normalized_index = (double)i / (kernel_size - 1); - r_kernel[i] = norm * exp(-normalized_index * normalized_index / sigma_sq); + r_kernel[i] = exp(-normalized_index * normalized_index / sigma_sq); if (i == 0) { sum += r_kernel[i]; } @@ -415,7 +416,7 @@ void get_1d_gauss_kernel(const float sigma, const int kernel_size, double *r_ker /* Normalize kernel values. */ for (int i = 0; i < kernel_size; i++) { - r_kernel[i] = r_kernel[i] / sum; + r_kernel[i] /= sum; } } @@ -426,14 +427,16 @@ void smooth_fcurve_segment(FCurve *fcu, const int kernel_size, double *kernel) { - for (int i = segment->start_index; i < segment->start_index + segment->length; i++) { - const int sample_index = (int)(fcu->bezt[i].vec[1][0] - - fcu->bezt[segment->start_index].vec[1][0]) + - kernel_size; - double filter_result = 0; + const int segment_end_index = segment->start_index + segment->length; + const int segment_start_x = fcu->bezt[segment->start_index].vec[1][0]; + for (int i = segment->start_index; i < segment_end_index; i++) { + const int sample_index = (int)(fcu->bezt[i].vec[1][0] - segment_start_x) + kernel_size; + double filter_result = samples[sample_index] * kernel[0]; /* Apply the kernel. */ - for (int j = -kernel_size; j <= kernel_size; j++) { - filter_result += samples[sample_index + j] * kernel[abs(j)]; + for (int j = 1; j <= kernel_size; j++) { + const double kernel_value = kernel[j]; + filter_result += samples[sample_index + j] * kernel_value; + filter_result += samples[sample_index - j] * kernel_value; } const float key_y_value = interpf((float)filter_result, samples[sample_index], factor); move_key(&fcu->bezt[i], key_y_value); diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h index 31ff429889d..69f103c6cba 100644 --- a/source/blender/editors/include/ED_keyframes_edit.h +++ b/source/blender/editors/include/ED_keyframes_edit.h @@ -429,7 +429,7 @@ void breakdown_fcurve_segment(struct FCurve *fcu, struct FCurveSegment *segment, * \param sigma The shape of the gauss distribution. * \param kernel_size How long the kernel array is. */ -void get_1d_gauss_kernel(const float sigma, int kernel_size, double *r_kernel); +void ED_ANIM_get_1d_gauss_kernel(const float sigma, int kernel_size, double *r_kernel); void smooth_fcurve_segment(struct FCurve *fcu, struct FCurveSegment *segment, float *samples, diff --git a/source/blender/editors/space_graph/graph_intern.h b/source/blender/editors/space_graph/graph_intern.h index 0459a83a7f8..7128f805a23 100644 --- a/source/blender/editors/space_graph/graph_intern.h +++ b/source/blender/editors/space_graph/graph_intern.h @@ -116,7 +116,7 @@ void GRAPH_OT_breakdown(struct wmOperatorType *ot); void GRAPH_OT_ease(struct wmOperatorType *ot); void GRAPH_OT_decimate(struct wmOperatorType *ot); void GRAPH_OT_blend_to_default(struct wmOperatorType *ot); -void GRAPH_OT_gauss_smooth(struct wmOperatorType *ot); +void GRAPH_OT_gaussian_smooth(struct wmOperatorType *ot); void GRAPH_OT_sample(struct wmOperatorType *ot); void GRAPH_OT_bake(struct wmOperatorType *ot); void GRAPH_OT_unbake(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c index fe103f6ed7a..b64884da06c 100644 --- a/source/blender/editors/space_graph/graph_ops.c +++ b/source/blender/editors/space_graph/graph_ops.c @@ -464,7 +464,7 @@ void graphedit_operatortypes(void) WM_operatortype_append(GRAPH_OT_breakdown); WM_operatortype_append(GRAPH_OT_ease); WM_operatortype_append(GRAPH_OT_blend_to_default); - WM_operatortype_append(GRAPH_OT_gauss_smooth); + WM_operatortype_append(GRAPH_OT_gaussian_smooth); WM_operatortype_append(GRAPH_OT_euler_filter); WM_operatortype_append(GRAPH_OT_delete); WM_operatortype_append(GRAPH_OT_duplicate); diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index 91ab34255d6..02dad6e607b 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -1073,22 +1073,23 @@ typedef struct tGaussOperatorData { ListBase anim_data; /* bAnimListElem */ } tGaussOperatorData; +/* Store data to smooth an FCurve segment. */ typedef struct tFCurveSegmentLink { struct tFCurveSegmentLink *prev, *next; FCurve *fcu; FCurveSegment *segment; - float *samples; + float *samples; /* Array of y-values of the FCurve segment. */ } tFCurveSegmentLink; -static void gauss_smooth_allocate_operator_data(tGraphSliderOp *gso, - const int filter_width, - const float sigma) +static void gaussian_smooth_allocate_operator_data(tGraphSliderOp *gso, + const int filter_width, + const float sigma) { tGaussOperatorData *operator_data = MEM_callocN(sizeof(tGaussOperatorData), "tGaussOperatorData"); const int kernel_size = filter_width + 1; double *kernel = MEM_callocN(sizeof(double) * kernel_size, "Gauss Kernel"); - get_1d_gauss_kernel(sigma, kernel_size, kernel); + ED_ANIM_get_1d_gauss_kernel(sigma, kernel_size, kernel); operator_data->kernel = kernel; ListBase anim_data = {NULL, NULL}; @@ -1119,7 +1120,7 @@ static void gauss_smooth_allocate_operator_data(tGraphSliderOp *gso, gso->operator_data = operator_data; } -static void gauss_smooth_free_operator_data(void *operator_data) +static void gaussian_smooth_free_operator_data(void *operator_data) { tGaussOperatorData *gauss_data = (tGaussOperatorData *)operator_data; LISTBASE_FOREACH (tFCurveSegmentLink *, segment_link, &gauss_data->segment_links) { @@ -1132,7 +1133,7 @@ static void gauss_smooth_free_operator_data(void *operator_data) MEM_freeN(gauss_data); } -static void gauss_smooth_draw_status_header(bContext *C, tGraphSliderOp *gso) +static void gaussian_smooth_draw_status_header(bContext *C, tGraphSliderOp *gso) { char status_str[UI_MAX_DRAW_STR]; char mode_str[32]; @@ -1156,7 +1157,7 @@ static void gauss_smooth_draw_status_header(bContext *C, tGraphSliderOp *gso) ED_workspace_status_text(C, status_str); } -static void gauss_smooth_modal_update(bContext *C, wmOperator *op) +static void gaussian_smooth_modal_update(bContext *C, wmOperator *op) { tGraphSliderOp *gso = op->customdata; @@ -1166,7 +1167,7 @@ static void gauss_smooth_modal_update(bContext *C, wmOperator *op) return; } - gauss_smooth_draw_status_header(C, gso); + gaussian_smooth_draw_status_header(C, gso); const float factor = slider_factor_get_and_remember(op); tGaussOperatorData *operator_data = (tGaussOperatorData *)gso->operator_data; @@ -1189,7 +1190,7 @@ static void gauss_smooth_modal_update(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); } -static int gauss_smooth_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int gaussian_smooth_invoke(bContext *C, wmOperator *op, const wmEvent *event) { const int invoke_result = graph_slider_invoke(C, op, event); @@ -1198,25 +1199,26 @@ static int gauss_smooth_invoke(bContext *C, wmOperator *op, const wmEvent *event } tGraphSliderOp *gso = op->customdata; - gso->modal_update = gauss_smooth_modal_update; + gso->modal_update = gaussian_smooth_modal_update; gso->factor_prop = RNA_struct_find_property(op->ptr, "factor"); const float sigma = RNA_float_get(op->ptr, "sigma"); const int filter_width = RNA_int_get(op->ptr, "filter_width"); - gauss_smooth_allocate_operator_data(gso, filter_width, sigma); - gso->free_operator_data = gauss_smooth_free_operator_data; + gaussian_smooth_allocate_operator_data(gso, filter_width, sigma); + gso->free_operator_data = gaussian_smooth_free_operator_data; ED_slider_allow_overshoot_set(gso->slider, false); - gauss_smooth_draw_status_header(C, gso); + ED_slider_factor_set(gso->slider, 0.0f); + gaussian_smooth_draw_status_header(C, gso); return invoke_result; } -static void gauss_smooth_graph_keys(bAnimContext *ac, - const float factor, - double *kernel, - const int filter_width) +static void gaussian_smooth_graph_keys(bAnimContext *ac, + const float factor, + double *kernel, + const int filter_width) { ListBase anim_data = {NULL, NULL}; ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype); @@ -1244,7 +1246,7 @@ static void gauss_smooth_graph_keys(bAnimContext *ac, ANIM_animdata_freelist(&anim_data); } -static int gauss_exec(bContext *C, wmOperator *op) +static int gaussian_smooth_exec(bContext *C, wmOperator *op) { bAnimContext ac; @@ -1255,9 +1257,9 @@ static int gauss_exec(bContext *C, wmOperator *op) const int filter_width = RNA_int_get(op->ptr, "filter_width"); const int kernel_size = filter_width + 1; double *kernel = MEM_callocN(sizeof(double) * kernel_size, "Gauss Kernel"); - get_1d_gauss_kernel(RNA_float_get(op->ptr, "sigma"), kernel_size, kernel); + ED_ANIM_get_1d_gauss_kernel(RNA_float_get(op->ptr, "sigma"), kernel_size, kernel); - gauss_smooth_graph_keys(&ac, factor, kernel, filter_width); + gaussian_smooth_graph_keys(&ac, factor, kernel, filter_width); MEM_freeN(kernel); @@ -1267,17 +1269,17 @@ static int gauss_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void GRAPH_OT_gauss_smooth(wmOperatorType *ot) +void GRAPH_OT_gaussian_smooth(wmOperatorType *ot) { /* Identifiers. */ - ot->name = "Gauss Smooth"; - ot->idname = "GRAPH_OT_gauss_smooth"; + ot->name = "Gaussian Smooth"; + ot->idname = "GRAPH_OT_gaussian_smooth"; ot->description = "Smooth the curve using a Gauss filter"; /* API callbacks. */ - ot->invoke = gauss_smooth_invoke; + ot->invoke = gaussian_smooth_invoke; ot->modal = graph_slider_modal; - ot->exec = gauss_exec; + ot->exec = gaussian_smooth_exec; ot->poll = graphop_editable_keyframes_poll; /* Flags. */ -- 2.30.2 From 58756c89368d1fb6ac9e9f3359d6071460a82e4f Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Fri, 24 Mar 2023 11:46:42 +0100 Subject: [PATCH 12/12] implement sybrens comments --- source/blender/editors/animation/keyframes_general.c | 2 +- source/blender/editors/space_graph/graph_slider_ops.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index bd33782aa17..526d509f50a 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -431,8 +431,8 @@ void smooth_fcurve_segment(FCurve *fcu, const int segment_start_x = fcu->bezt[segment->start_index].vec[1][0]; for (int i = segment->start_index; i < segment_end_index; i++) { const int sample_index = (int)(fcu->bezt[i].vec[1][0] - segment_start_x) + kernel_size; - double filter_result = samples[sample_index] * kernel[0]; /* Apply the kernel. */ + double filter_result = samples[sample_index] * kernel[0]; for (int j = 1; j <= kernel_size; j++) { const double kernel_value = kernel[j]; filter_result += samples[sample_index + j] * kernel_value; diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index 02dad6e607b..a006e37e3f1 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -195,7 +195,7 @@ static void graph_slider_exit(bContext *C, wmOperator *op) return; } - if (gso->operator_data) { + if (gso->free_operator_data != NULL) { gso->free_operator_data(gso->operator_data); } -- 2.30.2