diff --git a/scripts/startup/bl_ui/space_graph.py b/scripts/startup/bl_ui/space_graph.py index 373dcbacb86..d42a52e3ecc 100644 --- a/scripts/startup/bl_ui/space_graph.py +++ b/scripts/startup/bl_ui/space_graph.py @@ -325,6 +325,7 @@ class GRAPH_MT_key_blending(Menu): layout.operator("graph.blend_to_ease", text="Blend to Ease") layout.operator("graph.match_slope", text="Match Slope") layout.operator("graph.shear", text="Shear Keys") + layout.operator("graph.scale_average", text="Scale Average") class GRAPH_MT_key_smoothing(Menu): diff --git a/source/blender/editors/animation/keyframes_general.cc b/source/blender/editors/animation/keyframes_general.cc index cf464a757b8..34edfc38dd7 100644 --- a/source/blender/editors/animation/keyframes_general.cc +++ b/source/blender/editors/animation/keyframes_general.cc @@ -390,6 +390,25 @@ void blend_to_default_fcurve(PointerRNA *id_ptr, FCurve *fcu, const float factor /* ---------------- */ +void scale_average_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor) +{ + float y = 0; + + /* Find first the average of the y values to then use it in the final calculation. */ + for (int i = segment->start_index; i < segment->start_index + segment->length; i++) { + y += fcu->bezt[i].vec[1][1]; + } + + const float y_average = y / segment->length; + + for (int i = segment->start_index; i < segment->start_index + segment->length; i++) { + const float key_y_value = interpf(y_average, fcu->bezt[i].vec[1][1], 1 - factor); + BKE_fcurve_keyframe_move_value_with_handles(&fcu->bezt[i], key_y_value); + } +} + +/* ---------------- */ + struct ButterworthCoefficients { double *A, *d1, *d2; int filter_order; diff --git a/source/blender/editors/include/ED_keyframes_edit.hh b/source/blender/editors/include/ED_keyframes_edit.hh index f48ae110f7d..df5c858c2f1 100644 --- a/source/blender/editors/include/ED_keyframes_edit.hh +++ b/source/blender/editors/include/ED_keyframes_edit.hh @@ -430,6 +430,7 @@ ListBase find_fcurve_segments(FCurve *fcu); void clean_fcurve(bAnimContext *ac, bAnimListElem *ale, float thresh, bool cleardefault); void blend_to_neighbor_fcurve_segment(FCurve *fcu, FCurveSegment *segment, float factor); void breakdown_fcurve_segment(FCurve *fcu, FCurveSegment *segment, float factor); +void scale_average_fcurve_segment(struct FCurve *fcu, struct FCurveSegment *segment, float factor); /** * Get a 1D gauss kernel. Since the kernel is symmetrical, only calculates the positive side. diff --git a/source/blender/editors/space_graph/graph_intern.h b/source/blender/editors/space_graph/graph_intern.h index 6b3956d37de..e9f151fb486 100644 --- a/source/blender/editors/space_graph/graph_intern.h +++ b/source/blender/editors/space_graph/graph_intern.h @@ -123,6 +123,7 @@ void GRAPH_OT_blend_offset(struct wmOperatorType *ot); void GRAPH_OT_blend_to_ease(struct wmOperatorType *ot); void GRAPH_OT_match_slope(struct wmOperatorType *ot); void GRAPH_OT_shear(struct wmOperatorType *ot); +void GRAPH_OT_scale_average(struct wmOperatorType *ot); void GRAPH_OT_decimate(struct wmOperatorType *ot); void GRAPH_OT_blend_to_default(struct wmOperatorType *ot); void GRAPH_OT_butterworth_smooth(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_graph/graph_ops.cc b/source/blender/editors/space_graph/graph_ops.cc index c192cd30156..ccecceed828 100644 --- a/source/blender/editors/space_graph/graph_ops.cc +++ b/source/blender/editors/space_graph/graph_ops.cc @@ -470,6 +470,7 @@ void graphedit_operatortypes() WM_operatortype_append(GRAPH_OT_breakdown); WM_operatortype_append(GRAPH_OT_ease); WM_operatortype_append(GRAPH_OT_shear); + WM_operatortype_append(GRAPH_OT_scale_average); WM_operatortype_append(GRAPH_OT_blend_offset); WM_operatortype_append(GRAPH_OT_blend_to_ease); WM_operatortype_append(GRAPH_OT_match_slope); diff --git a/source/blender/editors/space_graph/graph_slider_ops.cc b/source/blender/editors/space_graph/graph_slider_ops.cc index 6de37b91c7d..93e90192cd3 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.cc +++ b/source/blender/editors/space_graph/graph_slider_ops.cc @@ -1457,6 +1457,94 @@ void GRAPH_OT_shear(wmOperatorType *ot) "Which end of the segment to use as a reference to shear from"); } +/* -------------------------------------------------------------------- */ +/** \name Scale Average Operator + * \{ */ + +static void scale_average_graph_keys(bAnimContext *ac, const float factor) +{ + apply_fcu_segment_function(ac, factor, scale_average_fcurve_segment); +} + +static void scale_average_modal_update(bContext *C, wmOperator *op) +{ + tGraphSliderOp *gso = static_cast(op->customdata); + + common_draw_status_header(C, gso, "Scale to Average"); + + /* Reset keyframes to the state at invoke. */ + reset_bezts(gso); + const float factor = slider_factor_get_and_remember(op); + scale_average_graph_keys(&gso->ac, factor); + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); +} + +static int scale_average_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 = static_cast(op->customdata); + gso->modal_update = scale_average_modal_update; + gso->factor_prop = RNA_struct_find_property(op->ptr, "factor"); + common_draw_status_header(C, gso, "Scale to Average"); + ED_slider_factor_bounds_set(gso->slider, 0, 2); + ED_slider_factor_set(gso->slider, 1.0f); + + return invoke_result; +} + +static int scale_average_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + + /* Get editor data. */ + if (ANIM_animdata_get_context(C, &ac) == 0) { + return OPERATOR_CANCELLED; + } + + const float factor = RNA_float_get(op->ptr, "factor"); + + scale_average_graph_keys(&ac, factor); + + /* Set notifier that keyframes have changed. */ + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GRAPH_OT_scale_average(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Scale Average Keyframes"; + ot->idname = "GRAPH_OT_scale_average"; + ot->description = + "Increase or decrease the value of selected keys \n\ + in relationship to their average"; + + /* API callbacks. */ + ot->invoke = scale_average_invoke; + ot->modal = graph_slider_modal; + ot->exec = scale_average_exec; + ot->poll = graphop_editable_keyframes_poll; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X; + + RNA_def_float_factor(ot->srna, + "factor", + 1.0f, + -FLT_MAX, + FLT_MAX, + "Scale Factor", + "The scale factor applied to the curve segments", + 0.0f, + 2.0f); +} + /** \} */ /* -------------------------------------------------------------------- */ /** \name Gauss Smooth Operator