Blender V5.0
graph_slider_ops.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2020 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
14
15#include <cfloat>
16#include <cstring>
17
18#include "MEM_guardedalloc.h"
19
20#include "BLI_listbase.h"
21#include "BLI_math_base.h"
22
23#include "DEG_depsgraph.hh"
24#include "DNA_anim_types.h"
25#include "DNA_curve_types.h"
26#include "DNA_scene_types.h"
27
28#include "RNA_access.hh"
29#include "RNA_define.hh"
30
31#include "BLT_translation.hh"
32
33#include "BKE_context.hh"
34#include "BKE_report.hh"
35
36#include "UI_interface.hh"
37
38#include "ED_anim_api.hh"
39#include "ED_keyframes_edit.hh"
40#include "ED_numinput.hh"
41#include "ED_screen.hh"
42#include "ED_util.hh"
43
44#include "WM_api.hh"
45#include "WM_types.hh"
46
47#include "ANIM_fcurve.hh"
48
49#include <fmt/format.h>
50
51#include "graph_intern.hh"
52
53/* -------------------------------------------------------------------- */
56
57/* Used to obtain a list of animation channels for the operators to work on. */
58#define OPERATOR_DATA_FILTER \
59 (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | \
60 ANIMFILTER_FOREDIT | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS)
61
62/* This data type is only used for modal operation. */
68
71
74
76
77 /* Each operator has a specific update function. */
79
80 /* If an operator stores custom data, it also needs to provide the function to clean it up. */
83
85};
86
91
93
94/* -------------------------------------------------------------------- */
97
103 const float factor,
104 void (*segment_function)(FCurve *fcu,
105 FCurveSegment *segment,
106 const float factor))
107{
108 ListBase anim_data = {nullptr, nullptr};
109
111 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
112 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
113 FCurve *fcu = (FCurve *)ale->key_data;
114 ListBase segments = find_fcurve_segments(fcu);
115
116 LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
117 segment_function(fcu, segment, factor);
118 }
119
120 ale->update |= ANIM_UPDATE_DEFAULT;
121 BLI_freelistN(&segments);
122 }
123
124 ANIM_animdata_update(ac, &anim_data);
125 ANIM_animdata_freelist(&anim_data);
126}
127
129{
131 status.item(IFACE_("Confirm"), ICON_MOUSE_LMB);
132 status.item(IFACE_("Cancel"), ICON_EVENT_ESC);
133 status.item(IFACE_("Adjust"), ICON_MOUSE_MOVE);
134 if (hasNumInput(&gso->num)) {
135 char str_ofs[NUM_STR_REP_LEN];
136 outputNumInput(&gso->num, str_ofs, gso->scene->unit);
137 status.item(str_ofs, ICON_NONE);
138 }
139 else {
141 }
142}
143
149{
150 ListBase anim_data = {nullptr, nullptr};
151 bAnimContext *ac = &gso->ac;
152
154 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
155
156 /* Loop through filtered data and copy the curves. */
157 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
158 const FCurve *fcu = (const FCurve *)ale->key_data;
159
160 if (fcu->bezt == nullptr) {
161 /* This curve is baked, skip it. */
162 continue;
163 }
164
166 BezTriple *bezts_copy = MEM_malloc_arrayN<BezTriple>(fcu->totvert, "bezts_copy_array");
167
168 copy->tot_vert = fcu->totvert;
169 memcpy(bezts_copy, fcu->bezt, sizeof(BezTriple) * fcu->totvert);
170
171 copy->bezt = bezts_copy;
172
173 LinkData *link = nullptr;
174
175 link = MEM_callocN<LinkData>("Bezt Link");
176 link->data = copy;
177
178 BLI_addtail(&gso->bezt_arr_list, link);
179 }
180
181 ANIM_animdata_freelist(&anim_data);
182}
183
184/* Overwrite the current bezts arrays with the original data. */
186{
187 ListBase anim_data = {nullptr, nullptr};
188 LinkData *link_bezt;
189 bAnimListElem *ale;
190
191 bAnimContext *ac = &gso->ac;
192
193 /* Filter data. */
195 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
196
197 /* Loop through filtered data and reset bezts. */
198 for (ale = static_cast<bAnimListElem *>(anim_data.first),
199 link_bezt = static_cast<LinkData *>(gso->bezt_arr_list.first);
200 ale;
201 ale = ale->next)
202 {
203 FCurve *fcu = (FCurve *)ale->key_data;
204
205 if (fcu->bezt == nullptr) {
206 /* This curve is baked, skip it. */
207 continue;
208 }
209
210 tBeztCopyData *data = static_cast<tBeztCopyData *>(link_bezt->data);
211
212 MEM_freeN(fcu->bezt);
213
214 fcu->bezt = MEM_malloc_arrayN<BezTriple>(data->tot_vert, __func__);
215 fcu->totvert = data->tot_vert;
216
217 memcpy(fcu->bezt, data->bezt, sizeof(BezTriple) * data->tot_vert);
218
219 link_bezt = link_bezt->next;
220 }
221
222 ANIM_animdata_freelist(&anim_data);
223}
224
230{
231 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
232 const float factor = ED_slider_factor_get(gso->slider);
233 RNA_property_float_set(op->ptr, gso->factor_prop, factor);
234 return factor;
235}
236
238
239/* -------------------------------------------------------------------- */
242
244{
245 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
246 wmWindow *win = CTX_wm_window(C);
247
248 /* If data exists, clear its data and exit. */
249 if (gso == nullptr) {
250 return;
251 }
252
253 if (gso->free_operator_data != nullptr) {
255 }
256
257 ScrArea *area = gso->area;
258 LinkData *link;
259
261
262 for (link = static_cast<LinkData *>(gso->bezt_arr_list.first); link != nullptr;
263 link = link->next)
264 {
265 tBeztCopyData *copy = static_cast<tBeztCopyData *>(link->data);
266 MEM_freeN(copy->bezt);
267 MEM_freeN(link->data);
268 }
269
271 MEM_freeN(gso);
272
273 /* Return to normal cursor and header status. */
275 ED_area_status_text(area, nullptr);
276
277 /* cleanup */
278 op->customdata = nullptr;
279}
280
282{
283 ListBase anim_data = {nullptr, nullptr};
284
285 bAnimContext *ac = &gso->ac;
287 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
288 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
289 DEG_id_tag_update(ale->fcurve_owner_id, ID_RECALC_ANIMATION);
290 }
291
292 ANIM_animdata_freelist(&anim_data);
293}
294
296{
297 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
298
299 const bool has_numinput = hasNumInput(&gso->num);
300
302 fmt::format("{} ({})",
303 WM_operatortype_name(op->type, op->ptr),
305 .c_str());
306
307 ED_slider_modal(gso->slider, event);
308
309 switch (event->type) {
310 /* Confirm */
311 case LEFTMOUSE:
312 case EVT_RETKEY:
313 case EVT_PADENTER: {
314 if (event->val == KM_PRESS) {
315 graph_slider_exit(C, op);
316
317 return OPERATOR_FINISHED;
318 }
319 break;
320 }
321
322 /* Cancel */
323 case EVT_ESCKEY:
324 case RIGHTMOUSE: {
325 if (event->val == KM_PRESS) {
326 reset_bezts(gso);
327
328 /* The owner id's of the FCurves need to be updated, else the animation will be stuck in
329 * the state prior to calling reset_bezt. */
330 update_depsgraph(gso);
331
333
334 graph_slider_exit(C, op);
335
336 return OPERATOR_CANCELLED;
337 }
338 break;
339 }
340
341 case EVT_TABKEY:
342 /* Switch between acting on different properties. If this is not handled
343 * by the caller, it's explicitly gobbled up here to avoid it being passed
344 * through via the 'default' case. */
345 break;
346
347 /* When the mouse is moved, the percentage and the keyframes update. */
348 case MOUSEMOVE: {
349 if (has_numinput == false) {
350 /* Do the update as specified by the operator. */
351 gso->modal_update(C, op);
352 }
353 break;
354 }
355 default: {
356 if ((event->val == KM_PRESS) || (ISKEYMODIFIER(event->type) && event->val == KM_RELEASE)) {
357
358 if (handleNumInput(C, &gso->num, event)) {
359 float value;
360 applyNumInput(&gso->num, &value);
361
362 /* Grab percentage from numeric input, and store this new value for redo
363 * NOTE: users see ints, while internally we use a 0-1 float. */
365 value = value / 100.0f;
366 }
367 ED_slider_factor_set(gso->slider, value);
368 RNA_property_float_set(op->ptr, gso->factor_prop, value);
369 }
370
371 gso->modal_update(C, op);
372 break;
373 }
374
375 /* Unhandled event - maybe it was some view manipulation? */
376 /* Allow to pass through. */
378 }
379 }
380
382}
383
384/* Allocate tGraphSliderOp and assign to op->customdata. */
386{
387 tGraphSliderOp *gso;
388
390
391 /* Init slide-op data. */
392 gso = static_cast<tGraphSliderOp *>(
393 op->customdata = MEM_callocN(sizeof(tGraphSliderOp), "tGraphSliderOp"));
394
395 /* Get editor data. */
396 if (ANIM_animdata_get_context(C, &gso->ac) == 0) {
397 graph_slider_exit(C, op);
398 return OPERATOR_CANCELLED;
399 }
400 gso->ac.reports = op->reports;
401
402 gso->scene = CTX_data_scene(C);
403 gso->area = CTX_wm_area(C);
404 gso->region = CTX_wm_region(C);
405
407
408 gso->slider = ED_slider_create(C);
409 ED_slider_init(gso->slider, event);
410
411 if (gso->bezt_arr_list.first == nullptr) {
412 BKE_report(op->reports, RPT_ERROR, "Cannot find keys to operate on");
413 graph_slider_exit(C, op);
414 return OPERATOR_CANCELLED;
415 }
416
419}
420
422
423/* -------------------------------------------------------------------- */
426
431
432static void decimate_graph_keys(bAnimContext *ac, float factor, float error_sq_max)
433{
434 ListBase anim_data = {nullptr, nullptr};
435
436 /* Filter data. */
438 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
439
440 /* Loop through filtered data and clean curves. */
441 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
442 if (!decimate_fcurve(ale, factor, error_sq_max)) {
443 /* The selection contains unsupported keyframe types! */
444 BKE_report(ac->reports, RPT_WARNING, "Decimate: Skipping non linear/Bézier keyframes!");
445 }
446
447 ale->update |= ANIM_UPDATE_DEFAULT;
448 }
449
450 ANIM_animdata_update(ac, &anim_data);
451 ANIM_animdata_freelist(&anim_data);
452}
453
454/* Draw a percentage indicator in workspace footer. */
456{
458 status.item(IFACE_("Confirm"), ICON_MOUSE_LMB);
459 status.item(IFACE_("Cancel"), ICON_EVENT_ESC);
460 status.item(IFACE_("Adjust"), ICON_MOUSE_MOVE);
461 if (hasNumInput(&gso->num)) {
462 char str_ofs[NUM_STR_REP_LEN];
463 outputNumInput(&gso->num, str_ofs, gso->scene->unit);
464 status.item(str_ofs, ICON_NONE);
465 }
466 else {
468 }
469}
470
472{
473 /* Perform decimate updates - in response to some user action
474 * (e.g. pressing a key or moving the mouse). */
475 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
476
478
479 /* Reset keyframe data (so we get back to the original state). */
480 reset_bezts(gso);
481
482 /* Apply... */
483 const float factor = slider_factor_get_and_remember(op);
484 /* We don't want to limit the decimation to a certain error margin. */
485 const float error_sq_max = FLT_MAX;
486 decimate_graph_keys(&gso->ac, factor, error_sq_max);
488}
489
491{
492 const wmOperatorStatus invoke_result = graph_slider_invoke(C, op, event);
493
494 if (invoke_result == OPERATOR_CANCELLED) {
495 return OPERATOR_CANCELLED;
496 }
497
498 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
499 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
501 ED_slider_allow_overshoot_set(gso->slider, false, false);
502
503 return invoke_result;
504}
505
507{
508 bAnimContext ac;
509
510 /* Get editor data. */
511 if (ANIM_animdata_get_context(C, &ac) == 0) {
512 return OPERATOR_CANCELLED;
513 }
514
515 tDecimModes mode = tDecimModes(RNA_enum_get(op->ptr, "mode"));
516 /* We want to be able to work on all available keyframes. */
517 float factor = 1.0f;
518 /* We don't want to limit the decimation to a certain error margin. */
519 float error_sq_max = FLT_MAX;
520
521 switch (mode) {
522 case DECIM_RATIO:
523 factor = RNA_float_get(op->ptr, "factor");
524 break;
525 case DECIM_ERROR:
526 error_sq_max = RNA_float_get(op->ptr, "remove_error_margin");
527 /* The decimate algorithm expects the error to be squared. */
528 error_sq_max *= error_sq_max;
529
530 break;
531 }
532
533 if (factor == 0.0f || error_sq_max == 0.0f) {
534 /* Nothing to remove. */
535 return OPERATOR_FINISHED;
536 }
537
538 decimate_graph_keys(&ac, factor, error_sq_max);
539
540 /* Set notifier that keyframes have changed. */
542
543 return OPERATOR_FINISHED;
544}
545
546static bool decimate_poll_property(const bContext * /*C*/, wmOperator *op, const PropertyRNA *prop)
547{
548 const char *prop_id = RNA_property_identifier(prop);
549 const int mode = RNA_enum_get(op->ptr, "mode");
550
551 if (STREQ(prop_id, "factor") && mode != DECIM_RATIO) {
552 return false;
553 }
554 if (STREQ(prop_id, "remove_error_margin") && mode != DECIM_ERROR) {
555 return false;
556 }
557
558 return true;
559}
560
561static std::string decimate_get_description(bContext * /*C*/,
562 wmOperatorType * /*ot*/,
564{
565
566 if (RNA_enum_get(ptr, "mode") == DECIM_ERROR) {
567 return TIP_(
568 "Decimate F-Curves by specifying how much they can deviate from the original curve");
569 }
570
571 /* Use default description. */
572 return "";
573}
574
577 "RATIO",
578 0,
579 "Ratio",
580 "Use a percentage to specify how many keyframes you want to remove"},
582 "ERROR",
583 0,
584 "Error Margin",
585 "Use an error margin to specify how much the curve is allowed to deviate from the original "
586 "path"},
587 {0, nullptr, 0, nullptr, nullptr},
588};
589
591{
592 /* Identifiers */
593 ot->name = "Decimate Keyframes";
594 ot->idname = "GRAPH_OT_decimate";
595 ot->description =
596 "Decimate F-Curves by removing keyframes that influence the curve shape the least";
597
598 /* API callbacks */
599 ot->poll_property = decimate_poll_property;
600 ot->get_description = decimate_get_description;
601 ot->invoke = decimate_invoke;
602 ot->modal = graph_slider_modal;
603 ot->exec = decimate_exec;
605
606 /* Flags */
608
609 /* Properties */
610 RNA_def_enum(ot->srna,
611 "mode",
614 "Mode",
615 "Which mode to use for decimation");
616
618 "factor",
619 1.0f / 3.0f,
620 0.0f,
621 1.0f,
622 "Factor",
623 "The ratio of keyframes to remove",
624 0.0f,
625 1.0f);
626 RNA_def_float(ot->srna,
627 "remove_error_margin",
628 0.0f,
629 0.0f,
630 FLT_MAX,
631 "Max Error Margin",
632 "How much the new decimated curve is allowed to deviate from the original",
633 0.0f,
634 10.0f);
635}
636
638
639/* -------------------------------------------------------------------- */
642
643static void blend_to_neighbor_graph_keys(bAnimContext *ac, const float factor)
644{
646}
647
649{
650 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
651
653
654 /* Reset keyframe data to the state at invoke. */
655 reset_bezts(gso);
656
657 const float factor = slider_factor_get_and_remember(op);
658 blend_to_neighbor_graph_keys(&gso->ac, factor);
659
661}
662
664{
665 const wmOperatorStatus invoke_result = graph_slider_invoke(C, op, event);
666
667 if (invoke_result == OPERATOR_CANCELLED) {
668 return invoke_result;
669 }
670
671 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
673 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
676 ED_slider_factor_set(gso->slider, 0.0f);
677
678 return invoke_result;
679}
680
682{
683 bAnimContext ac;
684
685 if (ANIM_animdata_get_context(C, &ac) == 0) {
686 return OPERATOR_CANCELLED;
687 }
688
689 const float factor = RNA_float_get(op->ptr, "factor");
690
691 blend_to_neighbor_graph_keys(&ac, factor);
692
693 /* Set notifier that keyframes have changed. */
695
696 return OPERATOR_FINISHED;
697}
698
700{
701 /* Identifiers. */
702 ot->name = "Blend to Neighbor";
703 ot->idname = "GRAPH_OT_blend_to_neighbor";
704 ot->description = "Blend selected keyframes to their left or right neighbor";
705
706 /* API callbacks. */
708 ot->modal = graph_slider_modal;
711
712 /* Flags. */
714
716 "factor",
717 0.0f,
718 -FLT_MAX,
719 FLT_MAX,
720 "Blend",
721 "The blend factor with 0 being the current frame",
722 -1.0f,
723 1.0f);
724}
725
727
728/* -------------------------------------------------------------------- */
731
732static void breakdown_graph_keys(bAnimContext *ac, float factor)
733{
735}
736
738{
739 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
740
742
743 /* Reset keyframe data to the state at invoke. */
744 reset_bezts(gso);
745 const float factor = slider_factor_get_and_remember(op);
746 breakdown_graph_keys(&gso->ac, factor);
748}
749
751{
752 const wmOperatorStatus invoke_result = graph_slider_invoke(C, op, event);
753
754 if (invoke_result == OPERATOR_CANCELLED) {
755 return invoke_result;
756 }
757
758 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
760 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
763 ED_slider_factor_set(gso->slider, 0.0f);
764
765 return invoke_result;
766}
767
769{
770 bAnimContext ac;
771
772 if (ANIM_animdata_get_context(C, &ac) == 0) {
773 return OPERATOR_CANCELLED;
774 }
775
776 const float factor = RNA_float_get(op->ptr, "factor");
777
778 breakdown_graph_keys(&ac, factor);
779
780 /* Set notifier that keyframes have changed. */
782
783 return OPERATOR_FINISHED;
784}
785
787{
788 /* Identifiers. */
789 ot->name = "Breakdown";
790 ot->idname = "GRAPH_OT_breakdown";
791 ot->description = "Move selected keyframes to an inbetween position relative to adjacent keys";
792
793 /* API callbacks. */
794 ot->invoke = breakdown_invoke;
795 ot->modal = graph_slider_modal;
796 ot->exec = breakdown_exec;
798
799 /* Flags. */
801
803 "factor",
804 0.0f,
805 -FLT_MAX,
806 FLT_MAX,
807 "Factor",
808 "Favor either the left or the right key",
809 -1.0f,
810 1.0f);
811}
812
814
815/* -------------------------------------------------------------------- */
818
819static void blend_to_default_graph_keys(bAnimContext *ac, const float factor)
820{
821 ListBase anim_data = {nullptr, nullptr};
823 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
824
825 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
826 FCurve *fcu = (FCurve *)ale->key_data;
827
828 /* Check if the curves actually have any points. */
829 if (fcu == nullptr || fcu->bezt == nullptr || fcu->totvert == 0) {
830 continue;
831 }
832
833 PointerRNA id_ptr = RNA_id_pointer_create(ale->id);
834
835 blend_to_default_fcurve(&id_ptr, fcu, factor);
836 ale->update |= ANIM_UPDATE_DEFAULT;
837 }
838
839 ANIM_animdata_update(ac, &anim_data);
840 ANIM_animdata_freelist(&anim_data);
841}
842
844{
845 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
846
848
849 /* Set notifier that keyframes have changed. */
850 reset_bezts(gso);
851 const float factor = ED_slider_factor_get(gso->slider);
852 RNA_property_float_set(op->ptr, gso->factor_prop, factor);
853 blend_to_default_graph_keys(&gso->ac, factor);
855}
856
858{
859 const wmOperatorStatus invoke_result = graph_slider_invoke(C, op, event);
860
861 if (invoke_result == OPERATOR_CANCELLED) {
862 return invoke_result;
863 }
864
865 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
867 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
869 ED_slider_factor_set(gso->slider, 0.0f);
870
871 return invoke_result;
872}
873
875{
876 bAnimContext ac;
877
878 if (ANIM_animdata_get_context(C, &ac) == 0) {
879 return OPERATOR_CANCELLED;
880 }
881
882 const float factor = RNA_float_get(op->ptr, "factor");
883
884 blend_to_default_graph_keys(&ac, factor);
885
886 /* Set notifier that keyframes have changed. */
888
889 return OPERATOR_FINISHED;
890}
891
893{
894 /* Identifiers. */
895 ot->name = "Blend to Default Value";
896 ot->idname = "GRAPH_OT_blend_to_default";
897 ot->description = "Blend selected keys to their default value from their current position";
898
899 /* API callbacks. */
900 ot->invoke = blend_to_default_invoke;
901 ot->modal = graph_slider_modal;
904
905 /* Flags. */
907
909 "factor",
910 0.0f,
911 -FLT_MAX,
912 FLT_MAX,
913 "Factor",
914 "How much to blend to the default value",
915 0.0f,
916 1.0f);
917}
918
919
920/* -------------------------------------------------------------------- */
923
924static void ease_graph_keys(bAnimContext *ac, const float factor, const float width)
925{
926 ListBase anim_data = {nullptr, nullptr};
927
929 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
930 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
931 FCurve *fcu = (FCurve *)ale->key_data;
932 ListBase segments = find_fcurve_segments(fcu);
933
934 LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
935 ease_fcurve_segment(fcu, segment, factor, width);
936 }
937
938 ale->update |= ANIM_UPDATE_DEFAULT;
939 BLI_freelistN(&segments);
940 }
941
942 ANIM_animdata_update(ac, &anim_data);
943 ANIM_animdata_freelist(&anim_data);
944}
945
947{
948 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
950 status.item(IFACE_("Confirm"), ICON_MOUSE_LMB);
951 status.item(IFACE_("Cancel"), ICON_EVENT_ESC);
952 status.item(IFACE_("Adjust"), ICON_MOUSE_MOVE);
953 if (hasNumInput(&gso->num)) {
954 char str_ofs[NUM_STR_REP_LEN];
955 outputNumInput(&gso->num, str_ofs, gso->scene->unit);
956 status.item(str_ofs, ICON_NONE);
957 }
958 else {
960 /* Operator specific functionality that extends beyond the slider. */
961 if (STREQ(RNA_property_identifier(gso->factor_prop), "factor")) {
962 status.item(IFACE_("Modify Sharpness"), ICON_EVENT_TAB);
963 }
964 else {
965 status.item(IFACE_("Modify Curve Bend"), ICON_EVENT_TAB);
966 }
967 }
968}
969
971{
972 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
973
975
976 /* Reset keyframes to the state at invoke. */
977 reset_bezts(gso);
978 float factor;
979 float width;
980 if (STREQ(RNA_property_identifier(gso->factor_prop), "factor")) {
982 width = RNA_float_get(op->ptr, "sharpness");
983 }
984 else {
985 factor = RNA_float_get(op->ptr, "factor");
987 }
988
989 ease_graph_keys(&gso->ac, factor, width);
991}
992
994{
995 if (event->val != KM_PRESS) {
996 return graph_slider_modal(C, op, event);
997 }
998
999 switch (event->type) {
1000 case EVT_TABKEY: {
1001 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1002 if (STREQ(RNA_property_identifier(gso->factor_prop), "factor")) {
1003 /* Switch to sharpness. */
1004 ED_slider_allow_overshoot_set(gso->slider, false, true);
1005 ED_slider_factor_bounds_set(gso->slider, 0.001f, 10);
1006 ED_slider_factor_set(gso->slider, RNA_float_get(op->ptr, "sharpness"));
1008 ED_slider_unit_set(gso->slider, "");
1009 gso->factor_prop = RNA_struct_find_property(op->ptr, "sharpness");
1010 }
1011 else {
1012 ED_slider_allow_overshoot_set(gso->slider, false, false);
1014 ED_slider_factor_set(gso->slider, 0.0f);
1015 ED_slider_factor_set(gso->slider, RNA_float_get(op->ptr, "factor"));
1017 ED_slider_unit_set(gso->slider, "%");
1018 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
1019 }
1020 ease_modal_update(C, op);
1021 break;
1022 }
1023
1024 default:
1025 return graph_slider_modal(C, op, event);
1026 }
1028}
1029
1031{
1032 const wmOperatorStatus invoke_result = graph_slider_invoke(C, op, event);
1033
1034 if (invoke_result == OPERATOR_CANCELLED) {
1035 return invoke_result;
1036 }
1037
1038 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1040 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
1042 ED_slider_allow_overshoot_set(gso->slider, false, false);
1044 ED_slider_factor_set(gso->slider, 0.0f);
1046
1047 return invoke_result;
1048}
1049
1051{
1052 bAnimContext ac;
1053
1054 if (ANIM_animdata_get_context(C, &ac) == 0) {
1055 return OPERATOR_CANCELLED;
1056 }
1057
1058 const float factor = RNA_float_get(op->ptr, "factor");
1059 const float width = RNA_float_get(op->ptr, "sharpness");
1060
1061 ease_graph_keys(&ac, factor, width);
1062
1064
1065 return OPERATOR_FINISHED;
1066}
1067
1069{
1070 /* Identifiers. */
1071 ot->name = "Ease Keyframes";
1072 ot->idname = "GRAPH_OT_ease";
1073 ot->description = "Align keyframes on a ease-in or ease-out curve";
1074
1075 /* API callbacks. */
1076 ot->invoke = ease_invoke;
1077 ot->modal = ease_modal;
1078 ot->exec = ease_exec;
1080
1081 /* Flags. */
1083
1085 "factor",
1086 0.0f,
1087 -FLT_MAX,
1088 FLT_MAX,
1089 "Curve Bend",
1090 "Defines if the keys should be aligned on an ease-in or ease-out curve",
1091 -1.0f,
1092 1.0f);
1093
1094 RNA_def_float(ot->srna,
1095 "sharpness",
1096 2.0f,
1097 0.001f,
1098 FLT_MAX,
1099 "Sharpness",
1100 "Higher values make the change more abrupt",
1101 0.01f,
1102 16.0f);
1103}
1104
1106
1107/* -------------------------------------------------------------------- */
1110
1111static void blend_offset_graph_keys(bAnimContext *ac, const float factor)
1112{
1114}
1115
1120
1122{
1123 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1124
1126
1127 /* Reset keyframes to the state at invoke. */
1128 reset_bezts(gso);
1129 const float factor = slider_factor_get_and_remember(op);
1130 blend_offset_graph_keys(&gso->ac, factor);
1132}
1133
1135{
1136 const wmOperatorStatus invoke_result = graph_slider_invoke(C, op, event);
1137
1138 if (invoke_result == OPERATOR_CANCELLED) {
1139 return invoke_result;
1140 }
1141
1142 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1144 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
1147 ED_slider_factor_set(gso->slider, 0.0f);
1148
1149 return invoke_result;
1150}
1151
1153{
1154 bAnimContext ac;
1155
1156 /* Get editor data. */
1157 if (ANIM_animdata_get_context(C, &ac) == 0) {
1158 return OPERATOR_CANCELLED;
1159 }
1160
1161 const float factor = RNA_float_get(op->ptr, "factor");
1162
1163 blend_offset_graph_keys(&ac, factor);
1164
1165 /* Set notifier that keyframes have changed. */
1167
1168 return OPERATOR_FINISHED;
1169}
1170
1172{
1173 /* Identifiers. */
1174 ot->name = "Blend Offset Keyframes";
1175 ot->idname = "GRAPH_OT_blend_offset";
1176 ot->description = "Shift selected keys to the value of the neighboring keys as a block";
1177
1178 /* API callbacks. */
1179 ot->invoke = blend_offset_invoke;
1180 ot->modal = graph_slider_modal;
1181 ot->exec = blend_offset_exec;
1183
1184 /* Flags. */
1186
1188 "factor",
1189 0.0f,
1190 -FLT_MAX,
1191 FLT_MAX,
1192 "Offset Factor",
1193 "Control which key to offset towards and how far",
1194 -1.0f,
1195 1.0f);
1196}
1197
1199
1200/* -------------------------------------------------------------------- */
1203
1204static void blend_to_ease_graph_keys(bAnimContext *ac, const float factor)
1205{
1207}
1208
1213
1215{
1216 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1217
1219
1220 /* Reset keyframes to the state at invoke. */
1221 reset_bezts(gso);
1222 const float factor = slider_factor_get_and_remember(op);
1223 blend_to_ease_graph_keys(&gso->ac, factor);
1225}
1226
1228{
1229 const wmOperatorStatus invoke_result = graph_slider_invoke(C, op, event);
1230
1231 if (invoke_result == OPERATOR_CANCELLED) {
1232 return invoke_result;
1233 }
1234
1235 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1237 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
1239 ED_slider_allow_overshoot_set(gso->slider, false, false);
1241 ED_slider_factor_set(gso->slider, 0.0f);
1242
1243 return invoke_result;
1244}
1245
1247{
1248 bAnimContext ac;
1249
1250 /* Get editor data. */
1251 if (ANIM_animdata_get_context(C, &ac) == 0) {
1252 return OPERATOR_CANCELLED;
1253 }
1254
1255 const float factor = RNA_float_get(op->ptr, "factor");
1256
1257 blend_to_ease_graph_keys(&ac, factor);
1258
1259 /* Set notifier that keyframes have changed. */
1261
1262 return OPERATOR_FINISHED;
1263}
1264
1266{
1267 /* Identifiers. */
1268 ot->name = "Blend to Ease Keyframes";
1269 ot->idname = "GRAPH_OT_blend_to_ease";
1270 ot->description = "Blends keyframes from current state to an ease-in or ease-out curve";
1271
1272 /* API callbacks. */
1273 ot->invoke = blend_to_ease_invoke;
1274 ot->modal = graph_slider_modal;
1275 ot->exec = blend_to_ease_exec;
1277
1278 /* Flags. */
1279 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1280
1282 "factor",
1283 0.0f,
1284 -FLT_MAX,
1285 FLT_MAX,
1286 "Blend",
1287 "Favor either original data or ease curve",
1288 -1.0f,
1289 1.0f);
1290}
1291
1293
1294/* -------------------------------------------------------------------- */
1297
1298static void match_slope_graph_keys(bAnimContext *ac, const float factor)
1299{
1300 ListBase anim_data = {nullptr, nullptr};
1301
1302 bool all_segments_valid = true;
1303
1305 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
1306 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
1307 FCurve *fcu = (FCurve *)ale->key_data;
1308 ListBase segments = find_fcurve_segments(fcu);
1309
1310 LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
1311 all_segments_valid = match_slope_fcurve_segment(fcu, segment, factor);
1312 }
1313
1314 ale->update |= ANIM_UPDATE_DEFAULT;
1315 BLI_freelistN(&segments);
1316 }
1317
1318 if (!all_segments_valid) {
1319 if (factor >= 0) {
1320 BKE_report(
1321 ac->reports, RPT_WARNING, "You need at least 2 keys to the right side of the selection");
1322 }
1323 else {
1324 BKE_report(
1325 ac->reports, RPT_WARNING, "You need at least 2 keys to the left side of the selection");
1326 }
1327 }
1328
1329 ANIM_animdata_update(ac, &anim_data);
1330 ANIM_animdata_freelist(&anim_data);
1331}
1332
1337
1339{
1340 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1341
1343
1344 /* Reset keyframes to the state at invoke. */
1345 reset_bezts(gso);
1346 const float factor = slider_factor_get_and_remember(op);
1347 match_slope_graph_keys(&gso->ac, factor);
1349}
1350
1352{
1353 const wmOperatorStatus invoke_result = graph_slider_invoke(C, op, event);
1354
1355 if (invoke_result == OPERATOR_CANCELLED) {
1356 return invoke_result;
1357 }
1358
1359 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1361 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
1363 ED_slider_allow_overshoot_set(gso->slider, false, false);
1365 ED_slider_factor_set(gso->slider, 0.0f);
1366
1367 return invoke_result;
1368}
1369
1371{
1372 bAnimContext ac;
1373
1374 /* Get editor data. */
1375 if (ANIM_animdata_get_context(C, &ac) == 0) {
1376 return OPERATOR_CANCELLED;
1377 }
1378 ac.reports = op->reports;
1379
1380 const float factor = RNA_float_get(op->ptr, "factor");
1381
1382 match_slope_graph_keys(&ac, factor);
1383
1384 /* Set notifier that keyframes have changed. */
1386
1387 return OPERATOR_FINISHED;
1388}
1389
1391{
1392 /* Identifiers. */
1393 ot->name = "Match Slope";
1394 ot->idname = "GRAPH_OT_match_slope";
1395 ot->description = "Blend selected keys to the slope of neighboring ones";
1396
1397 /* API callbacks. */
1398 ot->invoke = match_slope_invoke;
1399 ot->modal = graph_slider_modal;
1400 ot->exec = match_slope_exec;
1402
1403 /* Flags. */
1404 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1405
1407 "factor",
1408 0.0f,
1409 -FLT_MAX,
1410 FLT_MAX,
1411 "Factor",
1412 "Defines which keys to use as slope and how much to blend towards them",
1413 -1.0f,
1414 1.0f);
1415}
1416
1417/* -------------------------------------------------------------------- */
1420
1421static void time_offset_graph_keys(bAnimContext *ac, const float factor)
1422{
1424}
1425
1430
1432{
1433 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1434
1436
1437 /* Reset keyframes to the state at invoke. */
1438 reset_bezts(gso);
1439 const float factor = slider_factor_get_and_remember(op);
1440 time_offset_graph_keys(&gso->ac, factor);
1442}
1443
1445{
1446 const wmOperatorStatus invoke_result = graph_slider_invoke(C, op, event);
1447
1448 if (invoke_result == OPERATOR_CANCELLED) {
1449 return invoke_result;
1450 }
1451
1452 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1454 gso->factor_prop = RNA_struct_find_property(op->ptr, "frame_offset");
1456 ED_slider_factor_bounds_set(gso->slider, -10, 10);
1458 ED_slider_factor_set(gso->slider, 0.0f);
1460 ED_slider_unit_set(gso->slider, "Frames");
1461
1462 return invoke_result;
1463}
1464
1466{
1467 bAnimContext ac;
1468
1469 /* Get editor data. */
1470 if (ANIM_animdata_get_context(C, &ac) == 0) {
1471 return OPERATOR_CANCELLED;
1472 }
1473
1474 const float factor = RNA_float_get(op->ptr, "frame_offset");
1475
1476 time_offset_graph_keys(&ac, factor);
1477
1478 /* Set notifier that keyframes have changed. */
1480
1481 return OPERATOR_FINISHED;
1482}
1483
1485{
1486 /* Identifiers. */
1487 ot->name = "Time Offset Keyframes";
1488 ot->idname = "GRAPH_OT_time_offset";
1489 ot->description = "Shifts the value of selected keys in time";
1490
1491 /* API callbacks. */
1492 ot->invoke = time_offset_invoke;
1493 ot->modal = graph_slider_modal;
1494 ot->exec = time_offset_exec;
1496
1497 /* Flags. */
1499
1501 "frame_offset",
1502 0.0f,
1503 -FLT_MAX,
1504 FLT_MAX,
1505 "Frame Offset",
1506 "How far in frames to offset the animation",
1507 -10.0f,
1508 10.0f);
1509}
1510
1512
1513/* -------------------------------------------------------------------- */
1516
1519 "FROM_LEFT",
1520 0,
1521 "From Left",
1522 "Shear the keys using the left key as reference"},
1524 "FROM_RIGHT",
1525 0,
1526 "From Right",
1527 "Shear the keys using the right key as reference"},
1528 {0, nullptr, 0, nullptr, nullptr},
1529};
1530
1531static void shear_graph_keys(bAnimContext *ac, const float factor, tShearDirection direction)
1532{
1533 ListBase anim_data = {nullptr, nullptr};
1534
1536 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
1537 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
1538 FCurve *fcu = (FCurve *)ale->key_data;
1539 ListBase segments = find_fcurve_segments(fcu);
1540
1541 LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
1542 shear_fcurve_segment(fcu, segment, factor, direction);
1543 }
1544
1545 ale->update |= ANIM_UPDATE_DEFAULT;
1546 BLI_freelistN(&segments);
1547 }
1548
1549 ANIM_animdata_update(ac, &anim_data);
1550 ANIM_animdata_freelist(&anim_data);
1551}
1552
1554{
1556 status.item(IFACE_("Confirm"), ICON_MOUSE_LMB);
1557 status.item(IFACE_("Cancel"), ICON_EVENT_ESC);
1558 status.item(IFACE_("Adjust"), ICON_MOUSE_MOVE);
1559 if (hasNumInput(&gso->num)) {
1560 char str_ofs[NUM_STR_REP_LEN];
1561 outputNumInput(&gso->num, str_ofs, gso->scene->unit);
1562 status.item(str_ofs, ICON_NONE);
1563 }
1564 else {
1566 status.item(
1567 fmt::format("{} ({})",
1568 IFACE_("Direction"),
1569 direction == SHEAR_FROM_LEFT ? IFACE_("From Left") : IFACE_("From Right")),
1570 ICON_EVENT_D);
1571 }
1572}
1573
1575{
1576 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1577
1578 /* Reset keyframes to the state at invoke. */
1579 reset_bezts(gso);
1580 const float factor = slider_factor_get_and_remember(op);
1581 const tShearDirection direction = tShearDirection(RNA_enum_get(op->ptr, "direction"));
1582
1583 shear_draw_status_header(C, gso, direction);
1584
1585 shear_graph_keys(&gso->ac, factor, direction);
1587}
1588
1590{
1591 if (event->val != KM_PRESS) {
1592 return graph_slider_modal(C, op, event);
1593 }
1594
1595 switch (event->type) {
1596 case EVT_DKEY: {
1597 tShearDirection direction = tShearDirection(RNA_enum_get(op->ptr, "direction"));
1598 RNA_enum_set(op->ptr,
1599 "direction",
1601 shear_modal_update(C, op);
1602 break;
1603 }
1604
1605 default:
1606 return graph_slider_modal(C, op, event);
1607 break;
1608 }
1610}
1611
1613{
1614 const wmOperatorStatus invoke_result = graph_slider_invoke(C, op, event);
1615
1616 if (invoke_result == OPERATOR_CANCELLED) {
1617 return invoke_result;
1618 }
1619
1620 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1622 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
1623 const tShearDirection direction = tShearDirection(RNA_enum_get(op->ptr, "direction"));
1624
1625 shear_draw_status_header(C, gso, direction);
1627 ED_slider_factor_set(gso->slider, 0.0f);
1628
1629 return invoke_result;
1630}
1631
1633{
1634 bAnimContext ac;
1635
1636 /* Get editor data. */
1637 if (ANIM_animdata_get_context(C, &ac) == 0) {
1638 return OPERATOR_CANCELLED;
1639 }
1640
1641 const float factor = RNA_float_get(op->ptr, "factor");
1642 const tShearDirection direction = tShearDirection(RNA_enum_get(op->ptr, "direction"));
1643
1644 shear_graph_keys(&ac, factor, direction);
1645
1646 /* Set notifier that keyframes have changed. */
1648
1649 return OPERATOR_FINISHED;
1650}
1651
1653{
1654 /* Identifiers. */
1655 ot->name = "Shear Keyframes";
1656 ot->idname = "GRAPH_OT_shear";
1657 ot->description =
1658 "Affect the value of the keys linearly, keeping the same relationship between them using "
1659 "either the left or the right key as reference";
1660
1661 /* API callbacks. */
1662 ot->invoke = shear_invoke;
1663 ot->modal = shear_modal;
1664 ot->exec = shear_exec;
1666
1667 /* Flags. */
1669
1671 "factor",
1672 0.0f,
1673 -FLT_MAX,
1674 FLT_MAX,
1675 "Shear Factor",
1676 "The amount of shear to apply",
1677 -1.0f,
1678 1.0f);
1679
1680 RNA_def_enum(ot->srna,
1681 "direction",
1684 "Direction",
1685 "Which end of the segment to use as a reference to shear from");
1686}
1687
1688/* -------------------------------------------------------------------- */
1691
1692static void scale_average_graph_keys(bAnimContext *ac, const float factor)
1693{
1695}
1696
1698{
1699 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1700
1702
1703 /* Reset keyframes to the state at invoke. */
1704 reset_bezts(gso);
1705 const float factor = slider_factor_get_and_remember(op);
1706 scale_average_graph_keys(&gso->ac, factor);
1708}
1709
1711{
1712 const wmOperatorStatus invoke_result = graph_slider_invoke(C, op, event);
1713
1714 if (invoke_result == OPERATOR_CANCELLED) {
1715 return invoke_result;
1716 }
1717
1718 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1720 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
1723 ED_slider_factor_set(gso->slider, 1.0f);
1724
1725 return invoke_result;
1726}
1727
1729{
1730 bAnimContext ac;
1731
1732 /* Get editor data. */
1733 if (ANIM_animdata_get_context(C, &ac) == 0) {
1734 return OPERATOR_CANCELLED;
1735 }
1736
1737 const float factor = RNA_float_get(op->ptr, "factor");
1738
1739 scale_average_graph_keys(&ac, factor);
1740
1741 /* Set notifier that keyframes have changed. */
1743
1744 return OPERATOR_FINISHED;
1745}
1746
1748{
1749 /* Identifiers. */
1750 ot->name = "Scale Average Keyframes";
1751 ot->idname = "GRAPH_OT_scale_average";
1752 ot->description = "Scale selected key values by their combined average";
1753
1754 /* API callbacks. */
1755 ot->invoke = scale_average_invoke;
1756 ot->modal = graph_slider_modal;
1757 ot->exec = scale_average_exec;
1759
1760 /* Flags. */
1762
1764 "factor",
1765 1.0f,
1766 -FLT_MAX,
1767 FLT_MAX,
1768 "Scale Factor",
1769 "The scale factor applied to the curve segments",
1770 0.0f,
1771 2.0f);
1772}
1773
1775
1776/* -------------------------------------------------------------------- */
1779
1780/* It is necessary to store data for smoothing when running in modal, because the sampling of
1781 * FCurves shouldn't be done on every update. */
1783 double *kernel;
1784 ListBase segment_links; /* tFCurveSegmentLink */
1785 ListBase anim_data; /* bAnimListElem */
1786};
1787
1788/* Store data to smooth an FCurve segment. */
1793 /* Array of y-values. The length of the array equals the length of the
1794 * segment. */
1796 /* Array of y-values of the FCurve segment at regular intervals. */
1797 float *samples;
1799};
1800
1801/* Allocates data that has to be freed after. */
1802static float *back_up_key_y_values(const FCurveSegment *segment, const FCurve *fcu)
1803{
1804 float *original_y_values = MEM_calloc_arrayN<float>(segment->length,
1805 "Smooth FCurve original values");
1806 for (int i = 0; i < segment->length; i++) {
1807 original_y_values[i] = fcu->bezt[i + segment->start_index].vec[1][1];
1808 }
1809 return original_y_values;
1810}
1811
1813 const int filter_width,
1814 const float sigma)
1815{
1816 tGaussOperatorData *operator_data = MEM_callocN<tGaussOperatorData>("tGaussOperatorData");
1817 const int kernel_size = filter_width + 1;
1818 double *kernel = MEM_calloc_arrayN<double>(kernel_size, "Gauss Kernel");
1819 ED_ANIM_get_1d_gauss_kernel(sigma, kernel_size, kernel);
1820 operator_data->kernel = kernel;
1821
1822 ListBase anim_data = {nullptr, nullptr};
1824 &gso->ac, &anim_data, OPERATOR_DATA_FILTER, gso->ac.data, eAnimCont_Types(gso->ac.datatype));
1825
1826 ListBase segment_links = {nullptr, nullptr};
1827 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
1828 FCurve *fcu = (FCurve *)ale->key_data;
1829 ListBase fcu_segments = find_fcurve_segments(fcu);
1830 LISTBASE_FOREACH (FCurveSegment *, segment, &fcu_segments) {
1831 tFCurveSegmentLink *segment_link = MEM_callocN<tFCurveSegmentLink>("FCurve Segment Link");
1832 segment_link->fcu = fcu;
1833 segment_link->segment = segment;
1834 segment_link->original_y_values = back_up_key_y_values(segment, fcu);
1835 BezTriple left_bezt = fcu->bezt[segment->start_index];
1836 BezTriple right_bezt = fcu->bezt[segment->start_index + segment->length - 1];
1837 const int sample_count = int(right_bezt.vec[1][0] - left_bezt.vec[1][0]) +
1838 (filter_width * 2 + 1);
1839 float *samples = MEM_calloc_arrayN<float>(sample_count, "Smooth FCurve Op Samples");
1841 fcu, left_bezt.vec[1][0] - filter_width, 1, samples, sample_count);
1842 segment_link->samples = samples;
1843 segment_link->sample_count = sample_count;
1844 BLI_addtail(&segment_links, segment_link);
1845 }
1846 }
1847
1848 operator_data->anim_data = anim_data;
1849 operator_data->segment_links = segment_links;
1850 gso->operator_data = operator_data;
1851}
1852
1853static void gaussian_smooth_free_operator_data(void *operator_data)
1854{
1855 tGaussOperatorData *gauss_data = (tGaussOperatorData *)operator_data;
1856 LISTBASE_FOREACH (tFCurveSegmentLink *, segment_link, &gauss_data->segment_links) {
1857 MEM_freeN(segment_link->samples);
1858 MEM_freeN(segment_link->segment);
1859 MEM_freeN(segment_link->original_y_values);
1860 }
1861 MEM_freeN(gauss_data->kernel);
1862 BLI_freelistN(&gauss_data->segment_links);
1863 ANIM_animdata_freelist(&gauss_data->anim_data);
1864 MEM_freeN(gauss_data);
1865}
1866
1868{
1869 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1870
1871 bAnimContext ac;
1872
1873 if (ANIM_animdata_get_context(C, &ac) == 0) {
1874 return;
1875 }
1876
1878
1879 const float factor = slider_factor_get_and_remember(op);
1880 tGaussOperatorData *operator_data = (tGaussOperatorData *)gso->operator_data;
1881 const int filter_width = RNA_int_get(op->ptr, "filter_width");
1882
1883 LISTBASE_FOREACH (tFCurveSegmentLink *, segment, &operator_data->segment_links) {
1884 smooth_fcurve_segment(segment->fcu,
1885 segment->segment,
1886 segment->original_y_values,
1887 segment->samples,
1888 segment->sample_count,
1889 factor,
1890 filter_width,
1891 operator_data->kernel);
1892 }
1893
1894 LISTBASE_FOREACH (bAnimListElem *, ale, &operator_data->anim_data) {
1895 ale->update |= ANIM_UPDATE_DEFAULT;
1896 }
1897
1898 ANIM_animdata_update(&ac, &operator_data->anim_data);
1900}
1901
1903{
1904 const wmOperatorStatus invoke_result = graph_slider_invoke(C, op, event);
1905
1906 if (invoke_result == OPERATOR_CANCELLED) {
1907 return invoke_result;
1908 }
1909
1910 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1912 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
1913
1914 const float sigma = RNA_float_get(op->ptr, "sigma");
1915 const int filter_width = RNA_int_get(op->ptr, "filter_width");
1916
1917 gaussian_smooth_allocate_operator_data(gso, filter_width, sigma);
1919
1920 ED_slider_allow_overshoot_set(gso->slider, false, false);
1921 ED_slider_factor_set(gso->slider, 0.0f);
1923
1924 return invoke_result;
1925}
1926
1928 const float factor,
1929 double *kernel,
1930 const int filter_width)
1931{
1932 ListBase anim_data = {nullptr, nullptr};
1934 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
1935
1936 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
1937 FCurve *fcu = (FCurve *)ale->key_data;
1938 ListBase segments = find_fcurve_segments(fcu);
1939
1940 LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
1941 BezTriple left_bezt = fcu->bezt[segment->start_index];
1942 BezTriple right_bezt = fcu->bezt[segment->start_index + segment->length - 1];
1943 const int sample_count = int(right_bezt.vec[1][0] - left_bezt.vec[1][0]) +
1944 (filter_width * 2 + 1);
1945 float *samples = MEM_calloc_arrayN<float>(sample_count, "Smooth FCurve Op Samples");
1946 float *original_y_values = back_up_key_y_values(segment, fcu);
1948 fcu, left_bezt.vec[1][0] - filter_width, 1, samples, sample_count);
1950 fcu, segment, original_y_values, samples, sample_count, factor, filter_width, kernel);
1951 MEM_freeN(samples);
1952 MEM_freeN(original_y_values);
1953 }
1954
1955 BLI_freelistN(&segments);
1956 ale->update |= ANIM_UPDATE_DEFAULT;
1957 }
1958
1959 ANIM_animdata_update(ac, &anim_data);
1960 ANIM_animdata_freelist(&anim_data);
1961}
1962
1964{
1965 bAnimContext ac;
1966
1967 if (ANIM_animdata_get_context(C, &ac) == 0) {
1968 return OPERATOR_CANCELLED;
1969 }
1970 const float factor = RNA_float_get(op->ptr, "factor");
1971 const int filter_width = RNA_int_get(op->ptr, "filter_width");
1972 const int kernel_size = filter_width + 1;
1973 double *kernel = MEM_calloc_arrayN<double>(kernel_size, "Gauss Kernel");
1974 ED_ANIM_get_1d_gauss_kernel(RNA_float_get(op->ptr, "sigma"), kernel_size, kernel);
1975
1976 gaussian_smooth_graph_keys(&ac, factor, kernel, filter_width);
1977
1978 MEM_freeN(kernel);
1979
1980 /* Set notifier that keyframes have changed. */
1982
1983 return OPERATOR_FINISHED;
1984}
1985
1987{
1988 /* Identifiers. */
1989 ot->name = "Gaussian Smooth";
1990 ot->idname = "GRAPH_OT_gaussian_smooth";
1991 ot->description = "Smooth the curve using a Gaussian filter";
1992
1993 /* API callbacks. */
1994 ot->invoke = gaussian_smooth_invoke;
1995 ot->modal = graph_slider_modal;
1996 ot->exec = gaussian_smooth_exec;
1998
1999 /* Flags. */
2000 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2001
2003 "factor",
2004 1.0f,
2005 0.0f,
2006 FLT_MAX,
2007 "Factor",
2008 "How much to blend to the default value",
2009 0.0f,
2010 1.0f);
2011
2012 RNA_def_float(ot->srna,
2013 "sigma",
2014 0.33f,
2015 0.001f,
2016 FLT_MAX,
2017 "Sigma",
2018 "The shape of the gaussian distribution, lower values make it sharper",
2019 0.001f,
2020 100.0f);
2021
2022 RNA_def_int(ot->srna,
2023 "filter_width",
2024 6,
2025 1,
2026 64,
2027 "Filter Width",
2028 "How far to each side the operator will average the key values",
2029 1,
2030 32);
2031}
2032
2033
2034/* -------------------------------------------------------------------- */
2037
2043
2044static int btw_calculate_sample_count(const BezTriple *right_bezt,
2045 const BezTriple *left_bezt,
2046 const int filter_order,
2047 const int samples_per_frame)
2048{
2049 /* Adding a constant 60 frames to combat the issue that the phase delay is shifting data out of
2050 * the sample count range. This becomes an issue when running the filter backwards. */
2051 const int sample_count = (int(right_bezt->vec[1][0] - left_bezt->vec[1][0]) + 1 +
2052 (filter_order * 2)) *
2053 samples_per_frame +
2054 60;
2055 return sample_count;
2056}
2057
2059 const int filter_order,
2060 const int samples_per_frame)
2061{
2062 tBtwOperatorData *operator_data = MEM_callocN<tBtwOperatorData>("tBtwOperatorData");
2063
2064 operator_data->coefficients = ED_anim_allocate_butterworth_coefficients(filter_order);
2065
2066 ListBase anim_data = {nullptr, nullptr};
2068 &gso->ac, &anim_data, OPERATOR_DATA_FILTER, gso->ac.data, eAnimCont_Types(gso->ac.datatype));
2069
2070 ListBase segment_links = {nullptr, nullptr};
2071 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
2072 FCurve *fcu = (FCurve *)ale->key_data;
2073 ListBase fcu_segments = find_fcurve_segments(fcu);
2074
2075 LISTBASE_FOREACH (FCurveSegment *, segment, &fcu_segments) {
2076
2077 tFCurveSegmentLink *segment_link = MEM_callocN<tFCurveSegmentLink>("FCurve Segment Link");
2078 segment_link->fcu = fcu;
2079 segment_link->segment = segment;
2080 BezTriple left_bezt = fcu->bezt[segment->start_index];
2081 BezTriple right_bezt = fcu->bezt[segment->start_index + segment->length - 1];
2082 const int sample_count = btw_calculate_sample_count(
2083 &right_bezt, &left_bezt, filter_order, samples_per_frame);
2084 float *samples = MEM_calloc_arrayN<float>(sample_count, "Btw Smooth FCurve Op Samples");
2086 fcu, left_bezt.vec[1][0] - filter_order, samples_per_frame, samples, sample_count);
2087 segment_link->samples = samples;
2088 segment_link->sample_count = sample_count;
2089 BLI_addtail(&segment_links, segment_link);
2090 }
2091 }
2092
2093 operator_data->anim_data = anim_data;
2094 operator_data->segment_links = segment_links;
2095 gso->operator_data = operator_data;
2096}
2097
2098static void btw_smooth_free_operator_data(void *operator_data)
2099{
2100 tBtwOperatorData *btw_data = (tBtwOperatorData *)operator_data;
2101 LISTBASE_FOREACH (tFCurveSegmentLink *, segment_link, &btw_data->segment_links) {
2102 MEM_freeN(segment_link->samples);
2103 MEM_freeN(segment_link->segment);
2104 }
2106 BLI_freelistN(&btw_data->segment_links);
2108 MEM_freeN(btw_data);
2109}
2110
2112{
2113 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
2114
2115 bAnimContext ac;
2116
2117 if (ANIM_animdata_get_context(C, &ac) == 0) {
2118 return;
2119 }
2120
2122
2123 tBtwOperatorData *operator_data = (tBtwOperatorData *)gso->operator_data;
2124
2125 const float frame_rate = float(ac.scene->r.frs_sec) / ac.scene->r.frs_sec_base;
2126 const int samples_per_frame = RNA_int_get(op->ptr, "samples_per_frame");
2127 const float sampling_frequency = frame_rate * samples_per_frame;
2128
2129 const float cutoff_frequency = slider_factor_get_and_remember(op);
2130 const int blend_in_out = RNA_int_get(op->ptr, "blend_in_out");
2131
2133 cutoff_frequency, sampling_frequency, operator_data->coefficients);
2134
2135 LISTBASE_FOREACH (tFCurveSegmentLink *, segment, &operator_data->segment_links) {
2137 segment->segment,
2138 segment->samples,
2139 segment->sample_count,
2140 1,
2141 blend_in_out,
2142 samples_per_frame,
2143 operator_data->coefficients);
2144 }
2145
2146 LISTBASE_FOREACH (bAnimListElem *, ale, &operator_data->anim_data) {
2147 ale->update |= ANIM_UPDATE_DEFAULT;
2148 }
2149
2150 ANIM_animdata_update(&ac, &operator_data->anim_data);
2152}
2153
2155{
2156 const wmOperatorStatus invoke_result = graph_slider_invoke(C, op, event);
2157
2158 if (invoke_result == OPERATOR_CANCELLED) {
2159 return invoke_result;
2160 }
2161
2162 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
2164 gso->factor_prop = RNA_struct_find_property(op->ptr, "cutoff_frequency");
2165
2166 const int filter_order = RNA_int_get(op->ptr, "filter_order");
2167 const int samples_per_frame = RNA_int_get(op->ptr, "samples_per_frame");
2168
2169 btw_smooth_allocate_operator_data(gso, filter_order, samples_per_frame);
2171
2172 const float frame_rate = float(gso->scene->r.frs_sec) / gso->scene->r.frs_sec_base;
2173 const float sampling_frequency = frame_rate * samples_per_frame;
2174 ED_slider_factor_bounds_set(gso->slider, 0, sampling_frequency / 2);
2175 ED_slider_increment_step_set(gso->slider, sampling_frequency / 20);
2176 ED_slider_factor_set(gso->slider, RNA_float_get(op->ptr, "cutoff_frequency"));
2177 ED_slider_allow_overshoot_set(gso->slider, false, false);
2179 ED_slider_unit_set(gso->slider, "Hz");
2181
2182 return invoke_result;
2183}
2184
2186 const float factor,
2187 const int blend_in_out,
2188 float cutoff_frequency,
2189 const int filter_order,
2190 const int samples_per_frame)
2191{
2192 ListBase anim_data = {nullptr, nullptr};
2194 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
2195
2197
2198 const float frame_rate = float(ac->scene->r.frs_sec) / ac->scene->r.frs_sec_base;
2199 const float sampling_frequency = frame_rate * samples_per_frame;
2200 /* Clamp cutoff frequency to Nyquist Frequency. */
2201 cutoff_frequency = min_ff(cutoff_frequency, sampling_frequency / 2);
2202 ED_anim_calculate_butterworth_coefficients(cutoff_frequency, sampling_frequency, bw_coeff);
2203
2204 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
2205 FCurve *fcu = (FCurve *)ale->key_data;
2206 ListBase segments = find_fcurve_segments(fcu);
2207
2208 LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
2209 BezTriple left_bezt = fcu->bezt[segment->start_index];
2210 BezTriple right_bezt = fcu->bezt[segment->start_index + segment->length - 1];
2211 const int sample_count = btw_calculate_sample_count(
2212 &right_bezt, &left_bezt, filter_order, samples_per_frame);
2213 float *samples = MEM_calloc_arrayN<float>(sample_count, "Smooth FCurve Op Samples");
2215 fcu, left_bezt.vec[1][0] - filter_order, samples_per_frame, samples, sample_count);
2217 fcu, segment, samples, sample_count, factor, blend_in_out, samples_per_frame, bw_coeff);
2218 MEM_freeN(samples);
2219 }
2220
2221 BLI_freelistN(&segments);
2222 ale->update |= ANIM_UPDATE_DEFAULT;
2223 }
2224
2226 ANIM_animdata_update(ac, &anim_data);
2227 ANIM_animdata_freelist(&anim_data);
2228}
2229
2231{
2232 bAnimContext ac;
2233
2234 if (ANIM_animdata_get_context(C, &ac) == 0) {
2235 return OPERATOR_CANCELLED;
2236 }
2237 const float blend = RNA_float_get(op->ptr, "blend");
2238 const float cutoff_frequency = RNA_float_get(op->ptr, "cutoff_frequency");
2239 const int filter_order = RNA_int_get(op->ptr, "filter_order");
2240 const int samples_per_frame = RNA_int_get(op->ptr, "samples_per_frame");
2241 const int blend_in_out = RNA_int_get(op->ptr, "blend_in_out");
2243 &ac, blend, blend_in_out, cutoff_frequency, filter_order, samples_per_frame);
2244
2245 /* Set notifier that keyframes have changed. */
2247
2248 return OPERATOR_FINISHED;
2249}
2250
2252{
2253 /* Identifiers. */
2254 ot->name = "Butterworth Smooth";
2255 ot->idname = "GRAPH_OT_butterworth_smooth";
2256 ot->description = "Smooth an F-Curve while maintaining the general shape of the curve";
2257
2258 /* API callbacks. */
2259 ot->invoke = btw_smooth_invoke;
2260 ot->modal = graph_slider_modal;
2261 ot->exec = btw_smooth_exec;
2263
2264 /* Flags. */
2266
2267 RNA_def_float(ot->srna,
2268 "cutoff_frequency",
2269 3.0f,
2270 0.0f,
2271 FLT_MAX,
2272 "Frequency Cutoff (Hz)",
2273 "Lower values give a smoother curve",
2274 0.0f,
2275 FLT_MAX);
2276
2277 RNA_def_int(ot->srna,
2278 "filter_order",
2279 4,
2280 1,
2281 32,
2282 "Filter Order",
2283 "Higher values produce a harder frequency cutoff",
2284 1,
2285 16);
2286
2287 RNA_def_int(ot->srna,
2288 "samples_per_frame",
2289 1,
2290 1,
2291 64,
2292 "Samples per Frame",
2293 "How many samples to calculate per frame, helps with subframe data",
2294 1,
2295 16);
2296
2298 "blend",
2299 1.0f,
2300 0,
2301 FLT_MAX,
2302 "Blend",
2303 "How much to blend to the smoothed curve",
2304 0.0f,
2305 1.0f);
2306
2307 RNA_def_int(ot->srna,
2308 "blend_in_out",
2309 1,
2310 0,
2311 INT_MAX,
2312 "Blend In/Out",
2313 "Linearly blend the smooth data to the border frames of the selection",
2314 0,
2315 128);
2316}
2317
2318
2319/* -------------------------------------------------------------------- */
2322
2323static void push_pull_graph_keys(bAnimContext *ac, const float factor)
2324{
2326}
2327
2329{
2330 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
2331
2333
2334 /* Reset keyframes to the state at invoke. */
2335 reset_bezts(gso);
2336 const float factor = slider_factor_get_and_remember(op);
2337 push_pull_graph_keys(&gso->ac, factor);
2339}
2340
2342{
2343 const wmOperatorStatus invoke_result = graph_slider_invoke(C, op, event);
2344
2345 if (invoke_result == OPERATOR_CANCELLED) {
2346 return invoke_result;
2347 }
2348
2349 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
2351 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
2355
2356 return invoke_result;
2357}
2358
2360{
2361 bAnimContext ac;
2362
2363 /* Get editor data. */
2364 if (ANIM_animdata_get_context(C, &ac) == 0) {
2365 return OPERATOR_CANCELLED;
2366 }
2367
2368 const float factor = RNA_float_get(op->ptr, "factor");
2369
2370 push_pull_graph_keys(&ac, factor);
2371
2372 /* Set notifier that keyframes have changed. */
2374
2375 return OPERATOR_FINISHED;
2376}
2377
2379{
2380 /* Identifiers. */
2381 ot->name = "Push Pull Keyframes";
2382 ot->idname = "GRAPH_OT_push_pull";
2383 ot->description = "Exaggerate or minimize the value of the selected keys";
2384
2385 /* API callbacks. */
2386 ot->invoke = push_pull_invoke;
2387 ot->modal = graph_slider_modal;
2388 ot->exec = push_pull_exec;
2390
2391 /* Flags. */
2393
2395 "factor",
2396 1.0f,
2397 -FLT_MAX,
2398 FLT_MAX,
2399 "Factor",
2400 "Control how far to push or pull the keys",
2401 0.0f,
2402 2.0f);
2403}
2404
2405
2406/* -------------------------------------------------------------------- */
2409
2411 {int(FCurveSegmentAnchor::LEFT), "LEFT", 0, "From Left", ""},
2412 {int(FCurveSegmentAnchor::RIGHT), "RIGHT", 0, "From Right", ""},
2413 {0, nullptr, 0, nullptr, nullptr},
2414};
2415
2417 const float factor,
2418 const FCurveSegmentAnchor anchor)
2419{
2420 ListBase anim_data = {nullptr, nullptr};
2421
2423 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
2424 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
2425 FCurve *fcu = (FCurve *)ale->key_data;
2426 ListBase segments = find_fcurve_segments(fcu);
2427
2428 LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
2429 scale_from_fcurve_segment_neighbor(fcu, segment, factor, anchor);
2430 }
2431
2432 ale->update |= ANIM_UPDATE_DEFAULT;
2433 BLI_freelistN(&segments);
2434 }
2435
2436 ANIM_animdata_update(ac, &anim_data);
2437 ANIM_animdata_freelist(&anim_data);
2438}
2439
2441{
2442 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
2444 status.item(IFACE_("Confirm"), ICON_MOUSE_LMB);
2445 status.item(IFACE_("Cancel"), ICON_EVENT_ESC);
2446 status.item(IFACE_("Adjust"), ICON_MOUSE_MOVE);
2447
2448 if (hasNumInput(&gso->num)) {
2449 char str_ofs[NUM_STR_REP_LEN];
2450 outputNumInput(&gso->num, str_ofs, gso->scene->unit);
2451 status.item(str_ofs, ICON_NONE);
2452 }
2453 else {
2455 /* Operator specific functionality that extends beyond the slider. */
2456 const FCurveSegmentAnchor anchor = FCurveSegmentAnchor(RNA_enum_get(op->ptr, "anchor"));
2458 status.item(fmt::format("{} ({})",
2459 IFACE_("Direction"),
2460 anchor == FCurveSegmentAnchor::LEFT ? IFACE_("From Left") :
2461 IFACE_("From Right")),
2462 ICON_EVENT_D);
2463 }
2464}
2465
2467{
2468 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
2469
2471
2472 /* Reset keyframes to the state at invoke. */
2473 reset_bezts(gso);
2474 const float factor = slider_factor_get_and_remember(op);
2475 const FCurveSegmentAnchor anchor = FCurveSegmentAnchor(RNA_enum_get(op->ptr, "anchor"));
2476 scale_from_neighbor_graph_keys(&gso->ac, factor, anchor);
2478}
2479
2481 wmOperator *op,
2482 const wmEvent *event)
2483{
2484 if (event->val != KM_PRESS) {
2485 return graph_slider_modal(C, op, event);
2486 }
2487
2488 switch (event->type) {
2489 case EVT_DKEY: {
2491 switch (anchor) {
2493 RNA_enum_set(op->ptr, "anchor", int(FCurveSegmentAnchor::RIGHT));
2494 break;
2495
2497 RNA_enum_set(op->ptr, "anchor", int(FCurveSegmentAnchor::LEFT));
2498 break;
2499 }
2501 break;
2502 }
2503
2504 default:
2505 return graph_slider_modal(C, op, event);
2506 }
2508}
2509
2511 wmOperator *op,
2512 const wmEvent *event)
2513{
2514 const wmOperatorStatus invoke_result = graph_slider_invoke(C, op, event);
2515
2516 if (invoke_result == OPERATOR_CANCELLED) {
2517 return OPERATOR_CANCELLED;
2518 }
2519
2520 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
2522 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
2525 ED_slider_factor_set(gso->slider, 1.0f);
2526
2527 return invoke_result;
2528}
2529
2531{
2532 bAnimContext ac;
2533
2534 /* Get editor data. */
2535 if (ANIM_animdata_get_context(C, &ac) == 0) {
2536 return OPERATOR_CANCELLED;
2537 }
2538
2539 const float factor = RNA_float_get(op->ptr, "factor");
2540
2541 const FCurveSegmentAnchor anchor = FCurveSegmentAnchor(RNA_enum_get(op->ptr, "anchor"));
2542 scale_from_neighbor_graph_keys(&ac, factor, anchor);
2543
2544 /* Set notifier that keyframes have changed. */
2546
2547 return OPERATOR_FINISHED;
2548}
2549
2551{
2552 /* Identifiers. */
2553 ot->name = "Scale from Neighbor";
2554 ot->idname = "GRAPH_OT_scale_from_neighbor";
2555 ot->description =
2556 "Increase or decrease the value of selected keys in relationship to the neighboring one";
2557
2558 /* API callbacks. */
2563
2564 /* Flags. */
2566
2568 "factor",
2569 0.0f,
2570 -FLT_MAX,
2571 FLT_MAX,
2572 "Factor",
2573 "The factor to scale keys with",
2574 -1.0f,
2575 1.0f);
2576
2577 RNA_def_enum(ot->srna,
2578 "anchor",
2581 "Reference Key",
2582 "Which end of the segment to use as a reference to scale from");
2583}
2584
Functions to modify FCurves.
ScrArea * CTX_wm_area(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
@ RPT_ERROR
Definition BKE_report.hh:39
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define LISTBASE_FOREACH(type, var, list)
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
MINLINE float min_ff(float a, float b)
#define STREQ(a, b)
#define TIP_(msgid)
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_ANIMATION
Definition DNA_ID.h:1077
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
#define ANIM_UPDATE_DEFAULT
eAnimCont_Types
tShearDirection
@ SHEAR_FROM_RIGHT
@ SHEAR_FROM_LEFT
FCurveSegmentAnchor
#define NUM_STR_REP_LEN
bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
Definition numinput.cc:312
bool applyNumInput(NumInput *n, float *vec)
Definition numinput.cc:190
void outputNumInput(NumInput *n, char *str, const UnitSettings &unit_settings)
Definition numinput.cc:88
bool hasNumInput(const NumInput *n)
Definition numinput.cc:171
void ED_area_status_text(ScrArea *area, const char *str)
Definition area.cc:851
void ED_slider_init(tSlider *slider, const wmEvent *event)
Definition ed_draw.cc:477
void ED_slider_allow_overshoot_set(tSlider *slider, bool lower, bool upper)
Definition ed_draw.cc:616
@ SLIDER_MODE_PERCENT
Definition ED_util.hh:95
@ SLIDER_MODE_FLOAT
Definition ED_util.hh:95
SliderMode ED_slider_mode_get(const tSlider *slider)
Definition ed_draw.cc:645
void ED_slider_unit_set(tSlider *slider, const char *unit)
Definition ed_draw.cc:650
void ED_slider_mode_set(tSlider *slider, SliderMode mode)
Definition ed_draw.cc:640
void ED_slider_property_label_set(tSlider *slider, const char *property_label)
Definition ed_draw.cc:655
void ED_slider_destroy(bContext *C, tSlider *slider)
Definition ed_draw.cc:579
void ED_slider_increment_step_set(tSlider *slider, float increment_step)
Definition ed_draw.cc:606
tSlider * ED_slider_create(bContext *C)
Definition ed_draw.cc:433
bool ED_slider_modal(tSlider *slider, const wmEvent *event)
Definition ed_draw.cc:482
void ED_slider_status_get(const tSlider *slider, WorkspaceStatus &status)
Definition ed_draw.cc:563
void ED_slider_factor_bounds_set(tSlider *slider, float factor_bound_lower, float factor_bound_upper)
Definition ed_draw.cc:632
float ED_slider_factor_get(const tSlider *slider)
Definition ed_draw.cc:592
void ED_slider_factor_set(tSlider *slider, float factor)
Definition ed_draw.cc:597
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
@ KM_PRESS
Definition WM_types.hh:311
@ KM_RELEASE
Definition WM_types.hh:312
#define NC_ANIMATION
Definition WM_types.hh:388
@ OPTYPE_BLOCKING
Definition WM_types.hh:184
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
@ OPTYPE_GRAB_CURSOR_X
Definition WM_types.hh:190
#define NA_EDITED
Definition WM_types.hh:584
#define ND_KEYFRAME
Definition WM_types.hh:494
void ANIM_animdata_freelist(ListBase *anim_data)
Definition anim_deps.cc:463
void ANIM_animdata_update(bAnimContext *ac, ListBase *anim_data)
Definition anim_deps.cc:356
bool ANIM_animdata_get_context(const bContext *C, bAnimContext *ac)
size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, const eAnimFilter_Flags filter_mode, void *data, const eAnimCont_Types datatype)
BMesh const char void * data
nullptr float
bool graphop_editable_keyframes_poll(bContext *C)
static wmOperatorStatus breakdown_exec(bContext *C, wmOperator *op)
static void blend_offset_modal_update(bContext *C, wmOperator *op)
static const EnumPropertyItem scale_anchor_items[]
static wmOperatorStatus match_slope_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus scale_average_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus push_pull_exec(bContext *C, wmOperator *op)
void GRAPH_OT_butterworth_smooth(wmOperatorType *ot)
static void match_slope_modal_update(bContext *C, wmOperator *op)
static void match_slope_graph_keys(bAnimContext *ac, const float factor)
static bool decimate_poll_property(const bContext *, wmOperator *op, const PropertyRNA *prop)
static void reset_bezts(tGraphSliderOp *gso)
static wmOperatorStatus time_offset_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void blend_to_ease_modal_update(bContext *C, wmOperator *op)
static void btw_smooth_allocate_operator_data(tGraphSliderOp *gso, const int filter_order, const int samples_per_frame)
static wmOperatorStatus shear_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus blend_to_default_exec(bContext *C, wmOperator *op)
static void ease_modal_update(bContext *C, wmOperator *op)
void GRAPH_OT_time_offset(wmOperatorType *ot)
static void gaussian_smooth_free_operator_data(void *operator_data)
void GRAPH_OT_blend_to_neighbor(wmOperatorType *ot)
static wmOperatorStatus graph_slider_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void blend_to_ease_draw_status_header(bContext *C, tGraphSliderOp *gso)
void GRAPH_OT_blend_to_ease(wmOperatorType *ot)
static std::string decimate_get_description(bContext *, wmOperatorType *, PointerRNA *ptr)
static const EnumPropertyItem decimate_mode_items[]
static void common_draw_status_header(bContext *C, tGraphSliderOp *gso)
static wmOperatorStatus shear_exec(bContext *C, wmOperator *op)
static wmOperatorStatus scale_average_exec(bContext *C, wmOperator *op)
static wmOperatorStatus scale_from_neighbor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void scale_from_neighbor_draw_status_header(bContext *C, wmOperator *op)
static wmOperatorStatus gaussian_smooth_exec(bContext *C, wmOperator *op)
static void ease_graph_keys(bAnimContext *ac, const float factor, const float width)
static void blend_to_default_modal_update(bContext *C, wmOperator *op)
static void push_pull_modal_update(bContext *C, wmOperator *op)
static void shear_modal_update(bContext *C, wmOperator *op)
static wmOperatorStatus btw_smooth_exec(bContext *C, wmOperator *op)
static wmOperatorStatus breakdown_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void scale_average_graph_keys(bAnimContext *ac, const float factor)
@ DECIM_RATIO
@ DECIM_ERROR
static wmOperatorStatus match_slope_exec(bContext *C, wmOperator *op)
static wmOperatorStatus btw_smooth_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static const EnumPropertyItem shear_direction_items[]
static void btw_smooth_modal_update(bContext *C, wmOperator *op)
#define OPERATOR_DATA_FILTER
static wmOperatorStatus blend_to_ease_exec(bContext *C, wmOperator *op)
void GRAPH_OT_gaussian_smooth(wmOperatorType *ot)
static wmOperatorStatus gaussian_smooth_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void GRAPH_OT_blend_offset(wmOperatorType *ot)
static void decimate_modal_update(bContext *C, wmOperator *op)
static void shear_graph_keys(bAnimContext *ac, const float factor, tShearDirection direction)
static void apply_fcu_segment_function(bAnimContext *ac, const float factor, void(*segment_function)(FCurve *fcu, FCurveSegment *segment, const float factor))
static wmOperatorStatus graph_slider_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void scale_average_modal_update(bContext *C, wmOperator *op)
static wmOperatorStatus ease_exec(bContext *C, wmOperator *op)
static int btw_calculate_sample_count(const BezTriple *right_bezt, const BezTriple *left_bezt, const int filter_order, const int samples_per_frame)
void GRAPH_OT_breakdown(wmOperatorType *ot)
static wmOperatorStatus decimate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static float slider_factor_get_and_remember(wmOperator *op)
static void blend_to_default_graph_keys(bAnimContext *ac, const float factor)
static float * back_up_key_y_values(const FCurveSegment *segment, const FCurve *fcu)
static wmOperatorStatus push_pull_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void match_slope_draw_status_header(bContext *C, tGraphSliderOp *gso)
static void time_offset_modal_update(bContext *C, wmOperator *op)
static wmOperatorStatus decimate_exec(bContext *C, wmOperator *op)
static void breakdown_modal_update(bContext *C, wmOperator *op)
static void blend_offset_graph_keys(bAnimContext *ac, const float factor)
void GRAPH_OT_push_pull(wmOperatorType *ot)
static void shear_draw_status_header(bContext *C, tGraphSliderOp *gso, tShearDirection direction)
static wmOperatorStatus scale_from_neighbor_exec(bContext *C, wmOperator *op)
static void gaussian_smooth_allocate_operator_data(tGraphSliderOp *gso, const int filter_width, const float sigma)
static void btw_smooth_graph_keys(bAnimContext *ac, const float factor, const int blend_in_out, float cutoff_frequency, const int filter_order, const int samples_per_frame)
static void blend_to_neighbor_graph_keys(bAnimContext *ac, const float factor)
static wmOperatorStatus blend_offset_exec(bContext *C, wmOperator *op)
static void blend_to_neighbor_modal_update(bContext *C, wmOperator *op)
void GRAPH_OT_scale_average(wmOperatorType *ot)
static wmOperatorStatus time_offset_exec(bContext *C, wmOperator *op)
static wmOperatorStatus blend_to_ease_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void GRAPH_OT_match_slope(wmOperatorType *ot)
static void gaussian_smooth_modal_update(bContext *C, wmOperator *op)
static wmOperatorStatus blend_to_default_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void decimate_graph_keys(bAnimContext *ac, float factor, float error_sq_max)
void GRAPH_OT_decimate(wmOperatorType *ot)
void GRAPH_OT_ease(wmOperatorType *ot)
static void ease_draw_status_header(bContext *C, wmOperator *op)
static void breakdown_graph_keys(bAnimContext *ac, float factor)
static wmOperatorStatus ease_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void blend_offset_draw_status_header(bContext *C, tGraphSliderOp *gso)
static void blend_to_ease_graph_keys(bAnimContext *ac, const float factor)
static wmOperatorStatus ease_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus shear_modal(bContext *C, wmOperator *op, const wmEvent *event)
void GRAPH_OT_scale_from_neighbor(wmOperatorType *ot)
void GRAPH_OT_shear(wmOperatorType *ot)
static wmOperatorStatus blend_to_neighbor_exec(bContext *C, wmOperator *op)
void GRAPH_OT_blend_to_default(wmOperatorType *ot)
static void graph_slider_exit(bContext *C, wmOperator *op)
static wmOperatorStatus blend_offset_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void push_pull_graph_keys(bAnimContext *ac, const float factor)
static void store_original_bezt_arrays(tGraphSliderOp *gso)
static void time_offset_draw_status_header(bContext *C, tGraphSliderOp *gso)
static void update_depsgraph(tGraphSliderOp *gso)
static void decimate_draw_status(bContext *C, tGraphSliderOp *gso)
static void scale_from_neighbor_modal_update(bContext *C, wmOperator *op)
static wmOperatorStatus blend_to_neighbor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void btw_smooth_free_operator_data(void *operator_data)
static void time_offset_graph_keys(bAnimContext *ac, const float factor)
static void scale_from_neighbor_graph_keys(bAnimContext *ac, const float factor, const FCurveSegmentAnchor anchor)
static void gaussian_smooth_graph_keys(bAnimContext *ac, const float factor, double *kernel, const int filter_width)
static wmOperatorStatus scale_from_neighbor_modal(bContext *C, wmOperator *op, const wmEvent *event)
void smooth_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float *original_values, float *samples, const int sample_count, const float factor, const int kernel_size, const double *kernel)
void ED_anim_calculate_butterworth_coefficients(const float cutoff_frequency, const float sampling_frequency, ButterworthCoefficients *bw_coeff)
void butterworth_smooth_fcurve_segment(FCurve *fcu, FCurveSegment *segment, float *samples, const int sample_count, const float factor, const int blend_in_out, const int sample_rate, ButterworthCoefficients *bw_coeff)
bool decimate_fcurve(bAnimListElem *ale, float remove_ratio, float error_sq_max)
void breakdown_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
void ease_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor, const float width)
ButterworthCoefficients * ED_anim_allocate_butterworth_coefficients(const int filter_order)
void time_offset_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float frame_offset)
void ED_ANIM_get_1d_gauss_kernel(const float sigma, const int kernel_size, double *r_kernel)
void scale_average_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
void blend_offset_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
void shear_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor, tShearDirection direction)
void blend_to_ease_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
bool match_slope_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
void push_pull_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
void blend_to_default_fcurve(PointerRNA *id_ptr, FCurve *fcu, const float factor)
ListBase find_fcurve_segments(FCurve *fcu)
void scale_from_fcurve_segment_neighbor(FCurve *fcu, FCurveSegment *segment, const float factor, const FCurveSegmentAnchor anchor)
void blend_to_neighbor_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
void ED_anim_free_butterworth_coefficients(ButterworthCoefficients *bw_coeff)
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
void sample_fcurve_segment(const FCurve *fcu, float start_frame, float sample_rate, float *samples, int sample_count)
static void copy(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node)
const int status
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
const char * RNA_property_ui_name(const PropertyRNA *prop, const PointerRNA *ptr)
int RNA_int_get(PointerRNA *ptr, const char *name)
float RNA_float_get(PointerRNA *ptr, const char *name)
void RNA_property_float_set(PointerRNA *ptr, PropertyRNA *prop, float value)
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
int RNA_enum_get(PointerRNA *ptr, const char *name)
const char * RNA_property_identifier(const PropertyRNA *prop)
PointerRNA RNA_id_pointer_create(ID *id)
PropertyRNA * RNA_def_float_factor(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
#define FLT_MAX
Definition stdcycles.h:14
float vec[3][3]
BezTriple * bezt
unsigned int totvert
void * data
struct LinkData * next
void * first
struct RenderData r
struct UnitSettings unit
eAnimCont_Types datatype
ReportList * reports
bAnimListElem * next
ButterworthCoefficients * coefficients
PropertyRNA * factor_prop
void(* modal_update)(bContext *, wmOperator *)
void(* free_operator_data)(void *operator_data)
wmEventType type
Definition WM_types.hh:757
short val
Definition WM_types.hh:759
struct ReportList * reports
struct wmOperatorType * type
struct PointerRNA * ptr
i
Definition text_draw.cc:230
static int blend(const Tex *tex, const float texvec[3], TexResult *texres)
void WM_cursor_modal_set(wmWindow *win, int val)
void WM_cursor_modal_restore(wmWindow *win)
@ WM_CURSOR_EW_SCROLL
Definition wm_cursors.hh:54
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
#define ISKEYMODIFIER(event_type)
@ RIGHTMOUSE
@ EVT_TABKEY
@ EVT_PADENTER
@ MOUSEMOVE
@ LEFTMOUSE
@ EVT_ESCKEY
@ EVT_DKEY
@ EVT_RETKEY
PointerRNA * ptr
Definition wm_files.cc:4238
wmOperatorType * ot
Definition wm_files.cc:4237
std::string WM_operatortype_name(wmOperatorType *ot, PointerRNA *properties)