Blender V4.3
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
15#include <cfloat>
16#include <cstring>
17
18#include "MEM_guardedalloc.h"
19
20#include "BLI_listbase.h"
21#include "BLI_string.h"
22
23#include "DEG_depsgraph.hh"
24#include "DNA_anim_types.h"
25#include "DNA_scene_types.h"
26
27#include "RNA_access.hh"
28#include "RNA_define.hh"
29
30#include "BLT_translation.hh"
31
32#include "BKE_context.hh"
33
34#include "UI_interface.hh"
35
36#include "ED_anim_api.hh"
37#include "ED_keyframes_edit.hh"
38#include "ED_numinput.hh"
39#include "ED_screen.hh"
40#include "ED_util.hh"
41
42#include "WM_api.hh"
43#include "WM_types.hh"
44
45#include "ANIM_fcurve.hh"
46
47#include "graph_intern.hh"
48
49/* -------------------------------------------------------------------- */
53/* Used to obtain a list of animation channels for the operators to work on. */
54#define OPERATOR_DATA_FILTER \
55 (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FCURVESONLY | \
56 ANIMFILTER_FOREDIT | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS)
57
58/* This data type is only used for modal operation. */
64
67
70
72
73 /* Each operator has a specific update function. */
75
76 /* If an operator stores custom data, it also needs to provide the function to clean it up. */
79
81};
82
87
90/* -------------------------------------------------------------------- */
99 const float factor,
100 void (*segment_function)(FCurve *fcu,
101 FCurveSegment *segment,
102 const float factor))
103{
104 ListBase anim_data = {nullptr, nullptr};
105
107 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
108 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
109 FCurve *fcu = (FCurve *)ale->key_data;
110 ListBase segments = find_fcurve_segments(fcu);
111
112 LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
113 segment_function(fcu, segment, factor);
114 }
115
116 ale->update |= ANIM_UPDATE_DEFAULT;
117 BLI_freelistN(&segments);
118 }
119
120 ANIM_animdata_update(ac, &anim_data);
121 ANIM_animdata_freelist(&anim_data);
122}
123
124static void common_draw_status_header(bContext *C, tGraphSliderOp *gso, const char *operator_name)
125{
126 char status_str[UI_MAX_DRAW_STR];
127 char mode_str[32];
128 char slider_string[UI_MAX_DRAW_STR];
129
131
132 STRNCPY(mode_str, IFACE_(operator_name));
133
134 if (hasNumInput(&gso->num)) {
135 char str_ofs[NUM_STR_REP_LEN];
136
137 outputNumInput(&gso->num, str_ofs, &gso->scene->unit);
138
139 SNPRINTF(status_str, "%s: %s", mode_str, str_ofs);
140 }
141 else {
142 SNPRINTF(status_str, "%s: %s", mode_str, slider_string);
143 }
144
145 ED_workspace_status_text(C, status_str);
146}
147
153{
154 ListBase anim_data = {nullptr, nullptr};
155 bAnimContext *ac = &gso->ac;
156
158 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
159
160 /* Loop through filtered data and copy the curves. */
161 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
162 const FCurve *fcu = (const FCurve *)ale->key_data;
163
164 if (fcu->bezt == nullptr) {
165 /* This curve is baked, skip it. */
166 continue;
167 }
168
169 const int arr_size = sizeof(BezTriple) * fcu->totvert;
170
171 tBeztCopyData *copy = static_cast<tBeztCopyData *>(
172 MEM_mallocN(sizeof(tBeztCopyData), "bezts_copy"));
173 BezTriple *bezts_copy = static_cast<BezTriple *>(MEM_mallocN(arr_size, "bezts_copy_array"));
174
175 copy->tot_vert = fcu->totvert;
176 memcpy(bezts_copy, fcu->bezt, arr_size);
177
178 copy->bezt = bezts_copy;
179
180 LinkData *link = nullptr;
181
182 link = static_cast<LinkData *>(MEM_callocN(sizeof(LinkData), "Bezt Link"));
183 link->data = copy;
184
185 BLI_addtail(&gso->bezt_arr_list, link);
186 }
187
188 ANIM_animdata_freelist(&anim_data);
189}
190
191/* Overwrite the current bezts arrays with the original data. */
193{
194 ListBase anim_data = {nullptr, nullptr};
195 LinkData *link_bezt;
196 bAnimListElem *ale;
197
198 bAnimContext *ac = &gso->ac;
199
200 /* Filter data. */
202 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
203
204 /* Loop through filtered data and reset bezts. */
205 for (ale = static_cast<bAnimListElem *>(anim_data.first),
206 link_bezt = static_cast<LinkData *>(gso->bezt_arr_list.first);
207 ale;
208 ale = ale->next)
209 {
210 FCurve *fcu = (FCurve *)ale->key_data;
211
212 if (fcu->bezt == nullptr) {
213 /* This curve is baked, skip it. */
214 continue;
215 }
216
217 tBeztCopyData *data = static_cast<tBeztCopyData *>(link_bezt->data);
218
219 const int arr_size = sizeof(BezTriple) * data->tot_vert;
220
221 MEM_freeN(fcu->bezt);
222
223 fcu->bezt = static_cast<BezTriple *>(MEM_mallocN(arr_size, __func__));
224 fcu->totvert = data->tot_vert;
225
226 memcpy(fcu->bezt, data->bezt, arr_size);
227
228 link_bezt = link_bezt->next;
229 }
230
231 ANIM_animdata_freelist(&anim_data);
232}
233
239{
240 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
241 const float factor = ED_slider_factor_get(gso->slider);
242 RNA_property_float_set(op->ptr, gso->factor_prop, factor);
243 return factor;
244}
245
248/* -------------------------------------------------------------------- */
253{
254 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
255 wmWindow *win = CTX_wm_window(C);
256
257 /* If data exists, clear its data and exit. */
258 if (gso == nullptr) {
259 return;
260 }
261
262 if (gso->free_operator_data != nullptr) {
264 }
265
266 ScrArea *area = gso->area;
267 LinkData *link;
268
269 ED_slider_destroy(C, gso->slider);
270
271 for (link = static_cast<LinkData *>(gso->bezt_arr_list.first); link != nullptr;
272 link = link->next)
273 {
274 tBeztCopyData *copy = static_cast<tBeztCopyData *>(link->data);
275 MEM_freeN(copy->bezt);
276 MEM_freeN(link->data);
277 }
278
280 MEM_freeN(gso);
281
282 /* Return to normal cursor and header status. */
284 ED_area_status_text(area, nullptr);
285
286 /* cleanup */
287 op->customdata = nullptr;
288}
289
291{
292 ListBase anim_data = {nullptr, nullptr};
293
294 bAnimContext *ac = &gso->ac;
296 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
297 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
298 DEG_id_tag_update(ale->fcurve_owner_id, ID_RECALC_ANIMATION);
299 }
300
301 ANIM_animdata_freelist(&anim_data);
302}
303
304static int graph_slider_modal(bContext *C, wmOperator *op, const wmEvent *event)
305{
306 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
307
308 const bool has_numinput = hasNumInput(&gso->num);
309
310 ED_slider_modal(gso->slider, event);
311
312 switch (event->type) {
313 /* Confirm */
314 case LEFTMOUSE:
315 case EVT_RETKEY:
316 case EVT_PADENTER: {
317 if (event->val == KM_PRESS) {
318 graph_slider_exit(C, op);
319
320 return OPERATOR_FINISHED;
321 }
322 break;
323 }
324
325 /* Cancel */
326 case EVT_ESCKEY:
327 case RIGHTMOUSE: {
328 if (event->val == KM_PRESS) {
329 reset_bezts(gso);
330
331 /* The owner id's of the FCurves need to be updated, else the animation will be stuck in
332 * the state prior to calling reset_bezt. */
333 update_depsgraph(gso);
334
336
337 graph_slider_exit(C, op);
338
339 return OPERATOR_CANCELLED;
340 }
341 break;
342 }
343
344 case EVT_TABKEY:
345 /* Switch between acting on different properties. If this is not handled
346 * by the caller, it's explicitly gobbled up here to avoid it being passed
347 * through via the 'default' case. */
348 break;
349
350 /* When the mouse is moved, the percentage and the keyframes update. */
351 case MOUSEMOVE: {
352 if (has_numinput == false) {
353 /* Do the update as specified by the operator. */
354 gso->modal_update(C, op);
355 }
356 break;
357 }
358 default: {
359 if ((event->val == KM_PRESS) && handleNumInput(C, &gso->num, event)) {
360 float value;
361 applyNumInput(&gso->num, &value);
362
363 /* Grab percentage from numeric input, and store this new value for redo
364 * NOTE: users see ints, while internally we use a 0-1 float. */
366 value = value / 100.0f;
367 }
368 ED_slider_factor_set(gso->slider, value);
369 RNA_property_float_set(op->ptr, gso->factor_prop, value);
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. */
385static int graph_slider_invoke(bContext *C, wmOperator *op, const wmEvent *event)
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
401 gso->scene = CTX_data_scene(C);
402 gso->area = CTX_wm_area(C);
403 gso->region = CTX_wm_region(C);
404
406
407 gso->slider = ED_slider_create(C);
408 ED_slider_init(gso->slider, event);
409
410 if (gso->bezt_arr_list.first == nullptr) {
411 WM_report(RPT_ERROR, "Cannot find keys to operate on");
412 graph_slider_exit(C, op);
413 return OPERATOR_CANCELLED;
414 }
415
418}
419
422/* -------------------------------------------------------------------- */
430
431static void decimate_graph_keys(bAnimContext *ac, float factor, float error_sq_max)
432{
433 ListBase anim_data = {nullptr, nullptr};
434
435 /* Filter data. */
437 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
438
439 /* Loop through filtered data and clean curves. */
440 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
441 if (!decimate_fcurve(ale, factor, error_sq_max)) {
442 /* The selection contains unsupported keyframe types! */
443 WM_report(RPT_WARNING, "Decimate: Skipping non linear/Bézier keyframes!");
444 }
445
446 ale->update |= ANIM_UPDATE_DEFAULT;
447 }
448
449 ANIM_animdata_update(ac, &anim_data);
450 ANIM_animdata_freelist(&anim_data);
451}
452
453/* Draw a percentage indicator in workspace footer. */
455{
456 char status_str[UI_MAX_DRAW_STR];
457 char mode_str[32];
458 char slider_string[UI_MAX_DRAW_STR];
459
461
462 STRNCPY(mode_str, IFACE_("Decimate Keyframes"));
463
464 if (hasNumInput(&gso->num)) {
465 char str_ofs[NUM_STR_REP_LEN];
466
467 outputNumInput(&gso->num, str_ofs, &gso->scene->unit);
468
469 SNPRINTF(status_str, "%s: %s", mode_str, str_ofs);
470 }
471 else {
472 SNPRINTF(status_str, "%s: %s", mode_str, slider_string);
473 }
474
475 ED_workspace_status_text(C, status_str);
476}
477
479{
480 /* Perform decimate updates - in response to some user action
481 * (e.g. pressing a key or moving the mouse). */
482 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
483
484 decimate_draw_status(C, gso);
485
486 /* Reset keyframe data (so we get back to the original state). */
487 reset_bezts(gso);
488
489 /* Apply... */
490 const float factor = slider_factor_get_and_remember(op);
491 /* We don't want to limit the decimation to a certain error margin. */
492 const float error_sq_max = FLT_MAX;
493 decimate_graph_keys(&gso->ac, factor, error_sq_max);
495}
496
497static int decimate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
498{
499 const int invoke_result = graph_slider_invoke(C, op, event);
500
501 if (invoke_result == OPERATOR_CANCELLED) {
502 return OPERATOR_CANCELLED;
503 }
504
505 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
506 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
508 ED_slider_allow_overshoot_set(gso->slider, false, false);
509
510 return invoke_result;
511}
512
514{
515 bAnimContext ac;
516
517 /* Get editor data. */
518 if (ANIM_animdata_get_context(C, &ac) == 0) {
519 return OPERATOR_CANCELLED;
520 }
521
522 tDecimModes mode = tDecimModes(RNA_enum_get(op->ptr, "mode"));
523 /* We want to be able to work on all available keyframes. */
524 float factor = 1.0f;
525 /* We don't want to limit the decimation to a certain error margin. */
526 float error_sq_max = FLT_MAX;
527
528 switch (mode) {
529 case DECIM_RATIO:
530 factor = RNA_float_get(op->ptr, "factor");
531 break;
532 case DECIM_ERROR:
533 error_sq_max = RNA_float_get(op->ptr, "remove_error_margin");
534 /* The decimate algorithm expects the error to be squared. */
535 error_sq_max *= error_sq_max;
536
537 break;
538 }
539
540 if (factor == 0.0f || error_sq_max == 0.0f) {
541 /* Nothing to remove. */
542 return OPERATOR_FINISHED;
543 }
544
545 decimate_graph_keys(&ac, factor, error_sq_max);
546
547 /* Set notifier that keyframes have changed. */
549
550 return OPERATOR_FINISHED;
551}
552
553static bool decimate_poll_property(const bContext * /*C*/, wmOperator *op, const PropertyRNA *prop)
554{
555 const char *prop_id = RNA_property_identifier(prop);
556 const int mode = RNA_enum_get(op->ptr, "mode");
557
558 if (STREQ(prop_id, "factor") && mode != DECIM_RATIO) {
559 return false;
560 }
561 if (STREQ(prop_id, "remove_error_margin") && mode != DECIM_ERROR) {
562 return false;
563 }
564
565 return true;
566}
567
568static std::string decimate_get_description(bContext * /*C*/,
569 wmOperatorType * /*ot*/,
571{
572
573 if (RNA_enum_get(ptr, "mode") == DECIM_ERROR) {
574 return TIP_(
575 "Decimate F-Curves by specifying how much they can deviate from the original curve");
576 }
577
578 /* Use default description. */
579 return "";
580}
581
584 "RATIO",
585 0,
586 "Ratio",
587 "Use a percentage to specify how many keyframes you want to remove"},
589 "ERROR",
590 0,
591 "Error Margin",
592 "Use an error margin to specify how much the curve is allowed to deviate from the original "
593 "path"},
594 {0, nullptr, 0, nullptr, nullptr},
595};
596
598{
599 /* Identifiers */
600 ot->name = "Decimate Keyframes";
601 ot->idname = "GRAPH_OT_decimate";
602 ot->description =
603 "Decimate F-Curves by removing keyframes that influence the curve shape the least";
604
605 /* API callbacks */
612
613 /* Flags */
615
616 /* Properties */
618 "mode",
621 "Mode",
622 "Which mode to use for decimation");
623
625 "factor",
626 1.0f / 3.0f,
627 0.0f,
628 1.0f,
629 "Remove",
630 "The ratio of remaining keyframes after the operation",
631 0.0f,
632 1.0f);
634 "remove_error_margin",
635 0.0f,
636 0.0f,
637 FLT_MAX,
638 "Max Error Margin",
639 "How much the new decimated curve is allowed to deviate from the original",
640 0.0f,
641 10.0f);
642}
643
646/* -------------------------------------------------------------------- */
650static void blend_to_neighbor_graph_keys(bAnimContext *ac, const float factor)
651{
653}
654
656{
657 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
658
659 common_draw_status_header(C, gso, "Blend to Neighbor");
660
661 /* Reset keyframe data to the state at invoke. */
662 reset_bezts(gso);
663
664 const float factor = slider_factor_get_and_remember(op);
665 blend_to_neighbor_graph_keys(&gso->ac, factor);
666
668}
669
670static int blend_to_neighbor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
671{
672 const int invoke_result = graph_slider_invoke(C, op, event);
673
674 if (invoke_result == OPERATOR_CANCELLED) {
675 return invoke_result;
676 }
677
678 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
680 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
681 common_draw_status_header(C, gso, "Blend to Neighbor");
683 ED_slider_factor_set(gso->slider, 0.0f);
684
685 return invoke_result;
686}
687
689{
690 bAnimContext ac;
691
692 if (ANIM_animdata_get_context(C, &ac) == 0) {
693 return OPERATOR_CANCELLED;
694 }
695
696 const float factor = RNA_float_get(op->ptr, "factor");
697
698 blend_to_neighbor_graph_keys(&ac, factor);
699
700 /* Set notifier that keyframes have changed. */
702
703 return OPERATOR_FINISHED;
704}
705
707{
708 /* Identifiers. */
709 ot->name = "Blend to Neighbor";
710 ot->idname = "GRAPH_OT_blend_to_neighbor";
711 ot->description = "Blend selected keyframes to their left or right neighbor";
712
713 /* API callbacks. */
718
719 /* Flags. */
721
723 "factor",
724 0.0f,
725 -FLT_MAX,
726 FLT_MAX,
727 "Blend",
728 "The blend factor with 0 being the current frame",
729 -1.0f,
730 1.0f);
731}
732
735/* -------------------------------------------------------------------- */
739static void breakdown_graph_keys(bAnimContext *ac, float factor)
740{
742}
743
745{
746 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
747
748 common_draw_status_header(C, gso, "Breakdown");
749
750 /* Reset keyframe data to the state at invoke. */
751 reset_bezts(gso);
752 const float factor = slider_factor_get_and_remember(op);
753 breakdown_graph_keys(&gso->ac, factor);
755}
756
757static int breakdown_invoke(bContext *C, wmOperator *op, const wmEvent *event)
758{
759 const int invoke_result = graph_slider_invoke(C, op, event);
760
761 if (invoke_result == OPERATOR_CANCELLED) {
762 return invoke_result;
763 }
764
765 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
767 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
768 common_draw_status_header(C, gso, "Breakdown");
770 ED_slider_factor_set(gso->slider, 0.0f);
771
772 return invoke_result;
773}
774
776{
777 bAnimContext ac;
778
779 if (ANIM_animdata_get_context(C, &ac) == 0) {
780 return OPERATOR_CANCELLED;
781 }
782
783 const float factor = RNA_float_get(op->ptr, "factor");
784
785 breakdown_graph_keys(&ac, factor);
786
787 /* Set notifier that keyframes have changed. */
789
790 return OPERATOR_FINISHED;
791}
792
794{
795 /* Identifiers. */
796 ot->name = "Breakdown";
797 ot->idname = "GRAPH_OT_breakdown";
798 ot->description = "Move selected keyframes to an inbetween position relative to adjacent keys";
799
800 /* API callbacks. */
805
806 /* Flags. */
808
810 "factor",
811 0.0f,
812 -FLT_MAX,
813 FLT_MAX,
814 "Factor",
815 "Favor either the left or the right key",
816 -1.0f,
817 1.0f);
818}
819
822/* -------------------------------------------------------------------- */
826static void blend_to_default_graph_keys(bAnimContext *ac, const float factor)
827{
828 ListBase anim_data = {nullptr, nullptr};
830 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
831
832 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
833 FCurve *fcu = (FCurve *)ale->key_data;
834
835 /* Check if the curves actually have any points. */
836 if (fcu == nullptr || fcu->bezt == nullptr || fcu->totvert == 0) {
837 continue;
838 }
839
840 PointerRNA id_ptr = RNA_id_pointer_create(ale->id);
841
842 blend_to_default_fcurve(&id_ptr, fcu, factor);
843 ale->update |= ANIM_UPDATE_DEFAULT;
844 }
845
846 ANIM_animdata_update(ac, &anim_data);
847 ANIM_animdata_freelist(&anim_data);
848}
849
851{
852 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
853
854 common_draw_status_header(C, gso, "Blend to Default Value");
855
856 /* Set notifier that keyframes have changed. */
857 reset_bezts(gso);
858 const float factor = ED_slider_factor_get(gso->slider);
859 RNA_property_float_set(op->ptr, gso->factor_prop, factor);
860 blend_to_default_graph_keys(&gso->ac, factor);
862}
863
864static int blend_to_default_invoke(bContext *C, wmOperator *op, const wmEvent *event)
865{
866 const int invoke_result = graph_slider_invoke(C, op, event);
867
868 if (invoke_result == OPERATOR_CANCELLED) {
869 return invoke_result;
870 }
871
872 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
874 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
875 common_draw_status_header(C, gso, "Blend to Default Value");
876 ED_slider_factor_set(gso->slider, 0.0f);
877
878 return invoke_result;
879}
880
882{
883 bAnimContext ac;
884
885 if (ANIM_animdata_get_context(C, &ac) == 0) {
886 return OPERATOR_CANCELLED;
887 }
888
889 const float factor = RNA_float_get(op->ptr, "factor");
890
891 blend_to_default_graph_keys(&ac, factor);
892
893 /* Set notifier that keyframes have changed. */
895
896 return OPERATOR_FINISHED;
897}
898
900{
901 /* Identifiers. */
902 ot->name = "Blend to Default Value";
903 ot->idname = "GRAPH_OT_blend_to_default";
904 ot->description = "Blend selected keys to their default value from their current position";
905
906 /* API callbacks. */
911
912 /* Flags. */
914
916 "factor",
917 0.0f,
918 -FLT_MAX,
919 FLT_MAX,
920 "Factor",
921 "How much to blend to the default value",
922 0.0f,
923 1.0f);
924}
927/* -------------------------------------------------------------------- */
931static void ease_graph_keys(bAnimContext *ac, const float factor, const float width)
932{
933 ListBase anim_data = {nullptr, nullptr};
934
936 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
937 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
938 FCurve *fcu = (FCurve *)ale->key_data;
939 ListBase segments = find_fcurve_segments(fcu);
940
941 LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
942 ease_fcurve_segment(fcu, segment, factor, width);
943 }
944
945 ale->update |= ANIM_UPDATE_DEFAULT;
946 BLI_freelistN(&segments);
947 }
948
949 ANIM_animdata_update(ac, &anim_data);
950 ANIM_animdata_freelist(&anim_data);
951}
952
954{
955 char status_str[UI_MAX_DRAW_STR];
956 char mode_str[32];
957 char slider_string[UI_MAX_DRAW_STR];
958
959 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
961
962 /* Operator specific functionality that extends beyond the slider. */
963 char op_slider_string[UI_MAX_DRAW_STR];
964 if (STREQ(RNA_property_identifier(gso->factor_prop), "factor")) {
965 SNPRINTF(op_slider_string, "%s | %s", slider_string, IFACE_("[TAB] - Modify Sharpness"));
966 }
967 else {
968 SNPRINTF(op_slider_string, "%s | %s", slider_string, IFACE_("[TAB] - Modify Curve Bend"));
969 }
970
971 STRNCPY(mode_str, IFACE_("Ease Keys"));
972
973 if (hasNumInput(&gso->num)) {
974 char str_ofs[NUM_STR_REP_LEN];
975
976 outputNumInput(&gso->num, str_ofs, &gso->scene->unit);
977
978 SNPRINTF(status_str, "%s: %s", mode_str, str_ofs);
979 }
980 else {
981 SNPRINTF(status_str, "%s: %s", mode_str, op_slider_string);
982 }
983
984 ED_workspace_status_text(C, status_str);
985}
986
988{
989 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
990
992
993 /* Reset keyframes to the state at invoke. */
994 reset_bezts(gso);
995 float factor;
996 float width;
997 if (STREQ(RNA_property_identifier(gso->factor_prop), "factor")) {
999 width = RNA_float_get(op->ptr, "sharpness");
1000 }
1001 else {
1002 factor = RNA_float_get(op->ptr, "factor");
1004 }
1005
1006 ease_graph_keys(&gso->ac, factor, width);
1008}
1009
1010static int ease_modal(bContext *C, wmOperator *op, const wmEvent *event)
1011{
1012 if (event->val != KM_PRESS) {
1013 return graph_slider_modal(C, op, event);
1014 }
1015
1016 switch (event->type) {
1017 case EVT_TABKEY: {
1018 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1019 if (STREQ(RNA_property_identifier(gso->factor_prop), "factor")) {
1020 /* Switch to sharpness. */
1021 ED_slider_allow_overshoot_set(gso->slider, false, true);
1022 ED_slider_factor_bounds_set(gso->slider, 0.001f, 10);
1023 ED_slider_factor_set(gso->slider, RNA_float_get(op->ptr, "sharpness"));
1025 ED_slider_unit_set(gso->slider, "");
1026 gso->factor_prop = RNA_struct_find_property(op->ptr, "sharpness");
1027 }
1028 else {
1029 ED_slider_allow_overshoot_set(gso->slider, false, false);
1031 ED_slider_factor_set(gso->slider, 0.0f);
1032 ED_slider_factor_set(gso->slider, RNA_float_get(op->ptr, "factor"));
1034 ED_slider_unit_set(gso->slider, "%");
1035 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
1036 }
1038 ease_modal_update(C, op);
1039 break;
1040 }
1041
1042 default:
1043 return graph_slider_modal(C, op, event);
1044 }
1046}
1047
1048static int ease_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1049{
1050 const int invoke_result = graph_slider_invoke(C, op, event);
1051
1052 if (invoke_result == OPERATOR_CANCELLED) {
1053 return invoke_result;
1054 }
1055
1056 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1058 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
1060 ED_slider_allow_overshoot_set(gso->slider, false, false);
1062 ED_slider_factor_set(gso->slider, 0.0f);
1064
1065 return invoke_result;
1066}
1067
1068static int ease_exec(bContext *C, wmOperator *op)
1069{
1070 bAnimContext ac;
1071
1072 if (ANIM_animdata_get_context(C, &ac) == 0) {
1073 return OPERATOR_CANCELLED;
1074 }
1075
1076 const float factor = RNA_float_get(op->ptr, "factor");
1077 const float width = RNA_float_get(op->ptr, "sharpness");
1078
1079 ease_graph_keys(&ac, factor, width);
1080
1082
1083 return OPERATOR_FINISHED;
1084}
1085
1087{
1088 /* Identifiers. */
1089 ot->name = "Ease Keyframes";
1090 ot->idname = "GRAPH_OT_ease";
1091 ot->description = "Align keyframes on a ease-in or ease-out curve";
1092
1093 /* API callbacks. */
1095 ot->modal = ease_modal;
1096 ot->exec = ease_exec;
1098
1099 /* Flags. */
1101
1103 "factor",
1104 0.0f,
1105 -FLT_MAX,
1106 FLT_MAX,
1107 "Curve Bend",
1108 "Defines if the keys should be aligned on an ease-in or ease-out curve",
1109 -1.0f,
1110 1.0f);
1111
1113 "sharpness",
1114 2.0f,
1115 0.001f,
1116 FLT_MAX,
1117 "Sharpness",
1118 "Higher values make the change more abrupt",
1119 0.01f,
1120 16.0f);
1121}
1122
1125/* -------------------------------------------------------------------- */
1129static void blend_offset_graph_keys(bAnimContext *ac, const float factor)
1130{
1132}
1133
1135{
1136 common_draw_status_header(C, gso, "Blend Offset Keys");
1137}
1138
1140{
1141 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1142
1144
1145 /* Reset keyframes to the state at invoke. */
1146 reset_bezts(gso);
1147 const float factor = slider_factor_get_and_remember(op);
1148 blend_offset_graph_keys(&gso->ac, factor);
1150}
1151
1152static int blend_offset_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1153{
1154 const int invoke_result = graph_slider_invoke(C, op, event);
1155
1156 if (invoke_result == OPERATOR_CANCELLED) {
1157 return invoke_result;
1158 }
1159
1160 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1162 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
1165 ED_slider_factor_set(gso->slider, 0.0f);
1166
1167 return invoke_result;
1168}
1169
1171{
1172 bAnimContext ac;
1173
1174 /* Get editor data. */
1175 if (ANIM_animdata_get_context(C, &ac) == 0) {
1176 return OPERATOR_CANCELLED;
1177 }
1178
1179 const float factor = RNA_float_get(op->ptr, "factor");
1180
1181 blend_offset_graph_keys(&ac, factor);
1182
1183 /* Set notifier that keyframes have changed. */
1185
1186 return OPERATOR_FINISHED;
1187}
1188
1190{
1191 /* Identifiers. */
1192 ot->name = "Blend Offset Keyframes";
1193 ot->idname = "GRAPH_OT_blend_offset";
1194 ot->description = "Shift selected keys to the value of the neighboring keys as a block";
1195
1196 /* API callbacks. */
1201
1202 /* Flags. */
1204
1206 "factor",
1207 0.0f,
1208 -FLT_MAX,
1209 FLT_MAX,
1210 "Offset Factor",
1211 "Control which key to offset towards and how far",
1212 -1.0f,
1213 1.0f);
1214}
1215
1218/* -------------------------------------------------------------------- */
1222static void blend_to_ease_graph_keys(bAnimContext *ac, const float factor)
1223{
1225}
1226
1228{
1229 common_draw_status_header(C, gso, "Blend to Ease Keys");
1230}
1231
1233{
1234 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1235
1237
1238 /* Reset keyframes to the state at invoke. */
1239 reset_bezts(gso);
1240 const float factor = slider_factor_get_and_remember(op);
1241 blend_to_ease_graph_keys(&gso->ac, factor);
1243}
1244
1245static int blend_to_ease_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1246{
1247 const int invoke_result = graph_slider_invoke(C, op, event);
1248
1249 if (invoke_result == OPERATOR_CANCELLED) {
1250 return invoke_result;
1251 }
1252
1253 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1255 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
1257 ED_slider_allow_overshoot_set(gso->slider, false, false);
1259 ED_slider_factor_set(gso->slider, 0.0f);
1260
1261 return invoke_result;
1262}
1263
1265{
1266 bAnimContext ac;
1267
1268 /* Get editor data. */
1269 if (ANIM_animdata_get_context(C, &ac) == 0) {
1270 return OPERATOR_CANCELLED;
1271 }
1272
1273 const float factor = RNA_float_get(op->ptr, "factor");
1274
1275 blend_to_ease_graph_keys(&ac, factor);
1276
1277 /* Set notifier that keyframes have changed. */
1279
1280 return OPERATOR_FINISHED;
1281}
1282
1284{
1285 /* Identifiers. */
1286 ot->name = "Blend to Ease Keyframes";
1287 ot->idname = "GRAPH_OT_blend_to_ease";
1288 ot->description = "Blends keyframes from current state to an ease-in or ease-out curve";
1289
1290 /* API callbacks. */
1295
1296 /* Flags. */
1298
1300 "factor",
1301 0.0f,
1302 -FLT_MAX,
1303 FLT_MAX,
1304 "Blend",
1305 "Favor either original data or ease curve",
1306 -1.0f,
1307 1.0f);
1308}
1309
1312/* -------------------------------------------------------------------- */
1316static void match_slope_graph_keys(bAnimContext *ac, const float factor)
1317{
1318 ListBase anim_data = {nullptr, nullptr};
1319
1320 bool all_segments_valid = true;
1321
1323 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
1324 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
1325 FCurve *fcu = (FCurve *)ale->key_data;
1326 ListBase segments = find_fcurve_segments(fcu);
1327
1328 LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
1329 all_segments_valid = match_slope_fcurve_segment(fcu, segment, factor);
1330 }
1331
1332 ale->update |= ANIM_UPDATE_DEFAULT;
1333 BLI_freelistN(&segments);
1334 }
1335
1336 if (!all_segments_valid) {
1337 if (factor >= 0) {
1338 WM_report(RPT_WARNING, "You need at least 2 keys to the right side of the selection");
1339 }
1340 else {
1341 WM_report(RPT_WARNING, "You need at least 2 keys to the left side of the selection");
1342 }
1343 }
1344
1345 ANIM_animdata_update(ac, &anim_data);
1346 ANIM_animdata_freelist(&anim_data);
1347}
1348
1350{
1351 common_draw_status_header(C, gso, "Match Slope");
1352}
1353
1355{
1356 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1357
1359
1360 /* Reset keyframes to the state at invoke. */
1361 reset_bezts(gso);
1362 const float factor = slider_factor_get_and_remember(op);
1363 match_slope_graph_keys(&gso->ac, factor);
1365}
1366
1367static int match_slope_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1368{
1369 const int invoke_result = graph_slider_invoke(C, op, event);
1370
1371 if (invoke_result == OPERATOR_CANCELLED) {
1372 return invoke_result;
1373 }
1374
1375 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1377 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
1379 ED_slider_allow_overshoot_set(gso->slider, false, false);
1381 ED_slider_factor_set(gso->slider, 0.0f);
1382
1383 return invoke_result;
1384}
1385
1387{
1388 bAnimContext ac;
1389
1390 /* Get editor data. */
1391 if (ANIM_animdata_get_context(C, &ac) == 0) {
1392 return OPERATOR_CANCELLED;
1393 }
1394
1395 const float factor = RNA_float_get(op->ptr, "factor");
1396
1397 match_slope_graph_keys(&ac, factor);
1398
1399 /* Set notifier that keyframes have changed. */
1401
1402 return OPERATOR_FINISHED;
1403}
1404
1406{
1407 /* Identifiers. */
1408 ot->name = "Match Slope";
1409 ot->idname = "GRAPH_OT_match_slope";
1410 ot->description = "Blend selected keys to the slope of neighboring ones";
1411
1412 /* API callbacks. */
1417
1418 /* Flags. */
1420
1422 "factor",
1423 0.0f,
1424 -FLT_MAX,
1425 FLT_MAX,
1426 "Factor",
1427 "Defines which keys to use as slope and how much to blend towards them",
1428 -1.0f,
1429 1.0f);
1430}
1431
1432/* -------------------------------------------------------------------- */
1436static void time_offset_graph_keys(bAnimContext *ac, const float factor)
1437{
1439}
1440
1442{
1443 common_draw_status_header(C, gso, "Time Offset Keys");
1444}
1445
1447{
1448 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1449
1451
1452 /* Reset keyframes to the state at invoke. */
1453 reset_bezts(gso);
1454 const float factor = slider_factor_get_and_remember(op);
1455 time_offset_graph_keys(&gso->ac, factor);
1457}
1458
1459static int time_offset_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1460{
1461 const int invoke_result = graph_slider_invoke(C, op, event);
1462
1463 if (invoke_result == OPERATOR_CANCELLED) {
1464 return invoke_result;
1465 }
1466
1467 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1469 gso->factor_prop = RNA_struct_find_property(op->ptr, "frame_offset");
1471 ED_slider_factor_bounds_set(gso->slider, -10, 10);
1472 ED_slider_factor_set(gso->slider, 0.0f);
1474 ED_slider_unit_set(gso->slider, "Frames");
1475
1476 return invoke_result;
1477}
1478
1480{
1481 bAnimContext ac;
1482
1483 /* Get editor data. */
1484 if (ANIM_animdata_get_context(C, &ac) == 0) {
1485 return OPERATOR_CANCELLED;
1486 }
1487
1488 const float factor = RNA_float_get(op->ptr, "frame_offset");
1489
1490 time_offset_graph_keys(&ac, factor);
1491
1492 /* Set notifier that keyframes have changed. */
1494
1495 return OPERATOR_FINISHED;
1496}
1497
1499{
1500 /* Identifiers. */
1501 ot->name = "Time Offset Keyframes";
1502 ot->idname = "GRAPH_OT_time_offset";
1503 ot->description = "Shifts the value of selected keys in time";
1504
1505 /* API callbacks. */
1510
1511 /* Flags. */
1513
1515 "frame_offset",
1516 0.0f,
1517 -FLT_MAX,
1518 FLT_MAX,
1519 "Frame Offset",
1520 "How far in frames to offset the animation",
1521 -10.0f,
1522 10.0f);
1523}
1524
1527/* -------------------------------------------------------------------- */
1533 "FROM_LEFT",
1534 0,
1535 "From Left",
1536 "Shear the keys using the left key as reference"},
1538 "FROM_RIGHT",
1539 0,
1540 "From Right",
1541 "Shear the keys using the right key as reference"},
1542 {0, nullptr, 0, nullptr, nullptr},
1543};
1544
1545static void shear_graph_keys(bAnimContext *ac, const float factor, tShearDirection direction)
1546{
1547 ListBase anim_data = {nullptr, nullptr};
1548
1550 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
1551 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
1552 FCurve *fcu = (FCurve *)ale->key_data;
1553 ListBase segments = find_fcurve_segments(fcu);
1554
1555 LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
1556 shear_fcurve_segment(fcu, segment, factor, direction);
1557 }
1558
1559 ale->update |= ANIM_UPDATE_DEFAULT;
1560 BLI_freelistN(&segments);
1561 }
1562
1563 ANIM_animdata_update(ac, &anim_data);
1564 ANIM_animdata_freelist(&anim_data);
1565}
1566
1568{
1569 char status_str[UI_MAX_DRAW_STR];
1570 char mode_str[32];
1571 char slider_string[UI_MAX_DRAW_STR];
1573
1574 STRNCPY(mode_str, IFACE_("Shear Keys"));
1575
1576 if (hasNumInput(&gso->num)) {
1577 char str_ofs[NUM_STR_REP_LEN];
1578
1579 outputNumInput(&gso->num, str_ofs, &gso->scene->unit);
1580
1581 SNPRINTF(status_str, "%s: %s", mode_str, str_ofs);
1582 }
1583 else {
1584 const char *operator_string = IFACE_("D - Toggle Direction");
1585 SNPRINTF(status_str, "%s: %s | %s", mode_str, slider_string, operator_string);
1586 }
1587
1588 ED_workspace_status_text(C, status_str);
1589}
1590
1592{
1593 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1594
1596
1597 /* Reset keyframes to the state at invoke. */
1598 reset_bezts(gso);
1599 const float factor = slider_factor_get_and_remember(op);
1600 const tShearDirection direction = tShearDirection(RNA_enum_get(op->ptr, "direction"));
1601
1602 shear_graph_keys(&gso->ac, factor, direction);
1604}
1605
1606static int shear_modal(bContext *C, wmOperator *op, const wmEvent *event)
1607{
1608 if (event->val != KM_PRESS) {
1609 return graph_slider_modal(C, op, event);
1610 }
1611
1612 switch (event->type) {
1613 case EVT_DKEY: {
1614 tShearDirection direction = tShearDirection(RNA_enum_get(op->ptr, "direction"));
1615 RNA_enum_set(op->ptr,
1616 "direction",
1618 shear_modal_update(C, op);
1619 break;
1620 }
1621
1622 default:
1623 return graph_slider_modal(C, op, event);
1624 break;
1625 }
1627}
1628
1629static int shear_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1630{
1631 const int invoke_result = graph_slider_invoke(C, op, event);
1632
1633 if (invoke_result == OPERATOR_CANCELLED) {
1634 return invoke_result;
1635 }
1636
1637 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1639 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
1642 ED_slider_factor_set(gso->slider, 0.0f);
1643
1644 return invoke_result;
1645}
1646
1647static int shear_exec(bContext *C, wmOperator *op)
1648{
1649 bAnimContext ac;
1650
1651 /* Get editor data. */
1652 if (ANIM_animdata_get_context(C, &ac) == 0) {
1653 return OPERATOR_CANCELLED;
1654 }
1655
1656 const float factor = RNA_float_get(op->ptr, "factor");
1657 const tShearDirection direction = tShearDirection(RNA_enum_get(op->ptr, "direction"));
1658
1659 shear_graph_keys(&ac, factor, direction);
1660
1661 /* Set notifier that keyframes have changed. */
1663
1664 return OPERATOR_FINISHED;
1665}
1666
1668{
1669 /* Identifiers. */
1670 ot->name = "Shear Keyframes";
1671 ot->idname = "GRAPH_OT_shear";
1672 ot->description =
1673 "Affect the value of the keys linearly, keeping the same relationship between them using "
1674 "either the left or the right key as reference";
1675
1676 /* API callbacks. */
1678 ot->modal = shear_modal;
1679 ot->exec = shear_exec;
1681
1682 /* Flags. */
1684
1686 "factor",
1687 0.0f,
1688 -FLT_MAX,
1689 FLT_MAX,
1690 "Shear Factor",
1691 "The amount of shear to apply",
1692 -1.0f,
1693 1.0f);
1694
1696 "direction",
1699 "Direction",
1700 "Which end of the segment to use as a reference to shear from");
1701}
1702
1703/* -------------------------------------------------------------------- */
1707static void scale_average_graph_keys(bAnimContext *ac, const float factor)
1708{
1710}
1711
1713{
1714 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1715
1716 common_draw_status_header(C, gso, "Scale to Average");
1717
1718 /* Reset keyframes to the state at invoke. */
1719 reset_bezts(gso);
1720 const float factor = slider_factor_get_and_remember(op);
1721 scale_average_graph_keys(&gso->ac, factor);
1723}
1724
1725static int scale_average_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1726{
1727 const int invoke_result = graph_slider_invoke(C, op, event);
1728
1729 if (invoke_result == OPERATOR_CANCELLED) {
1730 return invoke_result;
1731 }
1732
1733 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1735 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
1736 common_draw_status_header(C, gso, "Scale to Average");
1738 ED_slider_factor_set(gso->slider, 1.0f);
1739
1740 return invoke_result;
1741}
1742
1744{
1745 bAnimContext ac;
1746
1747 /* Get editor data. */
1748 if (ANIM_animdata_get_context(C, &ac) == 0) {
1749 return OPERATOR_CANCELLED;
1750 }
1751
1752 const float factor = RNA_float_get(op->ptr, "factor");
1753
1754 scale_average_graph_keys(&ac, factor);
1755
1756 /* Set notifier that keyframes have changed. */
1758
1759 return OPERATOR_FINISHED;
1760}
1761
1763{
1764 /* Identifiers. */
1765 ot->name = "Scale Average Keyframes";
1766 ot->idname = "GRAPH_OT_scale_average";
1767 ot->description = "Scale selected key values by their combined average";
1768
1769 /* API callbacks. */
1774
1775 /* Flags. */
1777
1779 "factor",
1780 1.0f,
1781 -FLT_MAX,
1782 FLT_MAX,
1783 "Scale Factor",
1784 "The scale factor applied to the curve segments",
1785 0.0f,
1786 2.0f);
1787}
1788
1791/* -------------------------------------------------------------------- */
1795/* It is necessary to store data for smoothing when running in modal, because the sampling of
1796 * FCurves shouldn't be done on every update. */
1798 double *kernel;
1799 ListBase segment_links; /* tFCurveSegmentLink */
1800 ListBase anim_data; /* bAnimListElem */
1801};
1802
1803/* Store data to smooth an FCurve segment. */
1808 float *samples; /* Array of y-values of the FCurve segment. */
1810};
1811
1813 const int filter_width,
1814 const float sigma)
1815{
1816 tGaussOperatorData *operator_data = static_cast<tGaussOperatorData *>(
1817 MEM_callocN(sizeof(tGaussOperatorData), "tGaussOperatorData"));
1818 const int kernel_size = filter_width + 1;
1819 double *kernel = static_cast<double *>(
1820 MEM_callocN(sizeof(double) * kernel_size, "Gauss Kernel"));
1821 ED_ANIM_get_1d_gauss_kernel(sigma, kernel_size, kernel);
1822 operator_data->kernel = kernel;
1823
1824 ListBase anim_data = {nullptr, nullptr};
1826 &gso->ac, &anim_data, OPERATOR_DATA_FILTER, gso->ac.data, eAnimCont_Types(gso->ac.datatype));
1827
1828 ListBase segment_links = {nullptr, nullptr};
1829 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
1830 FCurve *fcu = (FCurve *)ale->key_data;
1831 ListBase fcu_segments = find_fcurve_segments(fcu);
1832 LISTBASE_FOREACH (FCurveSegment *, segment, &fcu_segments) {
1833 tFCurveSegmentLink *segment_link = static_cast<tFCurveSegmentLink *>(
1834 MEM_callocN(sizeof(tFCurveSegmentLink), "FCurve Segment Link"));
1835 segment_link->fcu = fcu;
1836 segment_link->segment = segment;
1837 BezTriple left_bezt = fcu->bezt[segment->start_index];
1838 BezTriple right_bezt = fcu->bezt[segment->start_index + segment->length - 1];
1839 const int sample_count = int(right_bezt.vec[1][0] - left_bezt.vec[1][0]) +
1840 (filter_width * 2 + 1);
1841 float *samples = static_cast<float *>(
1842 MEM_callocN(sizeof(float) * sample_count, "Smooth FCurve Op Samples"));
1844 fcu, left_bezt.vec[1][0] - filter_width, 1, samples, sample_count);
1845 segment_link->samples = samples;
1846 BLI_addtail(&segment_links, segment_link);
1847 }
1848 }
1849
1850 operator_data->anim_data = anim_data;
1851 operator_data->segment_links = segment_links;
1852 gso->operator_data = operator_data;
1853}
1854
1855static void gaussian_smooth_free_operator_data(void *operator_data)
1856{
1857 tGaussOperatorData *gauss_data = (tGaussOperatorData *)operator_data;
1858 LISTBASE_FOREACH (tFCurveSegmentLink *, segment_link, &gauss_data->segment_links) {
1859 MEM_freeN(segment_link->samples);
1860 MEM_freeN(segment_link->segment);
1861 }
1862 MEM_freeN(gauss_data->kernel);
1863 BLI_freelistN(&gauss_data->segment_links);
1864 ANIM_animdata_freelist(&gauss_data->anim_data);
1865 MEM_freeN(gauss_data);
1866}
1867
1869{
1870 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1871
1872 bAnimContext ac;
1873
1874 if (ANIM_animdata_get_context(C, &ac) == 0) {
1875 return;
1876 }
1877
1878 common_draw_status_header(C, gso, "Gaussian Smooth");
1879
1880 const float factor = slider_factor_get_and_remember(op);
1881 tGaussOperatorData *operator_data = (tGaussOperatorData *)gso->operator_data;
1882 const int filter_width = RNA_int_get(op->ptr, "filter_width");
1883
1884 LISTBASE_FOREACH (tFCurveSegmentLink *, segment, &operator_data->segment_links) {
1885 smooth_fcurve_segment(segment->fcu,
1886 segment->segment,
1887 segment->samples,
1888 factor,
1889 filter_width,
1890 operator_data->kernel);
1891 }
1892
1893 LISTBASE_FOREACH (bAnimListElem *, ale, &operator_data->anim_data) {
1894 ale->update |= ANIM_UPDATE_DEFAULT;
1895 }
1896
1897 ANIM_animdata_update(&ac, &operator_data->anim_data);
1899}
1900
1901static int gaussian_smooth_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1902{
1903 const int invoke_result = graph_slider_invoke(C, op, event);
1904
1905 if (invoke_result == OPERATOR_CANCELLED) {
1906 return invoke_result;
1907 }
1908
1909 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
1911 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
1912
1913 const float sigma = RNA_float_get(op->ptr, "sigma");
1914 const int filter_width = RNA_int_get(op->ptr, "filter_width");
1915
1916 gaussian_smooth_allocate_operator_data(gso, filter_width, sigma);
1918
1919 ED_slider_allow_overshoot_set(gso->slider, false, false);
1920 ED_slider_factor_set(gso->slider, 0.0f);
1921 common_draw_status_header(C, gso, "Gaussian Smooth");
1922
1923 return invoke_result;
1924}
1925
1927 const float factor,
1928 double *kernel,
1929 const int filter_width)
1930{
1931 ListBase anim_data = {nullptr, nullptr};
1933 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
1934
1935 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
1936 FCurve *fcu = (FCurve *)ale->key_data;
1937 ListBase segments = find_fcurve_segments(fcu);
1938
1939 LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
1940 BezTriple left_bezt = fcu->bezt[segment->start_index];
1941 BezTriple right_bezt = fcu->bezt[segment->start_index + segment->length - 1];
1942 const int sample_count = int(right_bezt.vec[1][0] - left_bezt.vec[1][0]) +
1943 (filter_width * 2 + 1);
1944 float *samples = static_cast<float *>(
1945 MEM_callocN(sizeof(float) * sample_count, "Smooth FCurve Op Samples"));
1947 fcu, left_bezt.vec[1][0] - filter_width, 1, samples, sample_count);
1948 smooth_fcurve_segment(fcu, segment, samples, factor, filter_width, kernel);
1949 MEM_freeN(samples);
1950 }
1951
1952 BLI_freelistN(&segments);
1953 ale->update |= ANIM_UPDATE_DEFAULT;
1954 }
1955
1956 ANIM_animdata_update(ac, &anim_data);
1957 ANIM_animdata_freelist(&anim_data);
1958}
1959
1961{
1962 bAnimContext ac;
1963
1964 if (ANIM_animdata_get_context(C, &ac) == 0) {
1965 return OPERATOR_CANCELLED;
1966 }
1967 const float factor = RNA_float_get(op->ptr, "factor");
1968 const int filter_width = RNA_int_get(op->ptr, "filter_width");
1969 const int kernel_size = filter_width + 1;
1970 double *kernel = static_cast<double *>(
1971 MEM_callocN(sizeof(double) * kernel_size, "Gauss Kernel"));
1972 ED_ANIM_get_1d_gauss_kernel(RNA_float_get(op->ptr, "sigma"), kernel_size, kernel);
1973
1974 gaussian_smooth_graph_keys(&ac, factor, kernel, filter_width);
1975
1976 MEM_freeN(kernel);
1977
1978 /* Set notifier that keyframes have changed. */
1980
1981 return OPERATOR_FINISHED;
1982}
1983
1985{
1986 /* Identifiers. */
1987 ot->name = "Gaussian Smooth";
1988 ot->idname = "GRAPH_OT_gaussian_smooth";
1989 ot->description = "Smooth the curve using a Gaussian filter";
1990
1991 /* API callbacks. */
1996
1997 /* Flags. */
1999
2001 "factor",
2002 1.0f,
2003 0.0f,
2004 FLT_MAX,
2005 "Factor",
2006 "How much to blend to the default value",
2007 0.0f,
2008 1.0f);
2009
2011 "sigma",
2012 0.33f,
2013 0.001f,
2014 FLT_MAX,
2015 "Sigma",
2016 "The shape of the gaussian distribution, lower values make it sharper",
2017 0.001f,
2018 100.0f);
2019
2021 "filter_width",
2022 6,
2023 1,
2024 64,
2025 "Filter Width",
2026 "How far to each side the operator will average the key values",
2027 1,
2028 32);
2029}
2032/* -------------------------------------------------------------------- */
2041
2042static int btw_calculate_sample_count(const BezTriple *right_bezt,
2043 const BezTriple *left_bezt,
2044 const int filter_order,
2045 const int samples_per_frame)
2046{
2047 /* Adding a constant 60 frames to combat the issue that the phase delay is shifting data out of
2048 * the sample count range. This becomes an issue when running the filter backwards. */
2049 const int sample_count = (int(right_bezt->vec[1][0] - left_bezt->vec[1][0]) + 1 +
2050 (filter_order * 2)) *
2051 samples_per_frame +
2052 60;
2053 return sample_count;
2054}
2055
2057 const int filter_order,
2058 const int samples_per_frame)
2059{
2060 tBtwOperatorData *operator_data = static_cast<tBtwOperatorData *>(
2061 MEM_callocN(sizeof(tBtwOperatorData), "tBtwOperatorData"));
2062
2063 operator_data->coefficients = ED_anim_allocate_butterworth_coefficients(filter_order);
2064
2065 ListBase anim_data = {nullptr, nullptr};
2067 &gso->ac, &anim_data, OPERATOR_DATA_FILTER, gso->ac.data, eAnimCont_Types(gso->ac.datatype));
2068
2069 ListBase segment_links = {nullptr, nullptr};
2070 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
2071 FCurve *fcu = (FCurve *)ale->key_data;
2072 ListBase fcu_segments = find_fcurve_segments(fcu);
2073
2074 LISTBASE_FOREACH (FCurveSegment *, segment, &fcu_segments) {
2075
2076 tFCurveSegmentLink *segment_link = static_cast<tFCurveSegmentLink *>(
2077 MEM_callocN(sizeof(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 = static_cast<float *>(
2085 MEM_callocN(sizeof(float) * sample_count, "Btw Smooth FCurve Op Samples"));
2087 fcu, left_bezt.vec[1][0] - filter_order, samples_per_frame, samples, sample_count);
2088 segment_link->samples = samples;
2089 segment_link->sample_count = sample_count;
2090 BLI_addtail(&segment_links, segment_link);
2091 }
2092 }
2093
2094 operator_data->anim_data = anim_data;
2095 operator_data->segment_links = segment_links;
2096 gso->operator_data = operator_data;
2097}
2098
2099static void btw_smooth_free_operator_data(void *operator_data)
2100{
2101 tBtwOperatorData *btw_data = (tBtwOperatorData *)operator_data;
2102 LISTBASE_FOREACH (tFCurveSegmentLink *, segment_link, &btw_data->segment_links) {
2103 MEM_freeN(segment_link->samples);
2104 MEM_freeN(segment_link->segment);
2105 }
2107 BLI_freelistN(&btw_data->segment_links);
2109 MEM_freeN(btw_data);
2110}
2111
2113{
2114 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
2115
2116 bAnimContext ac;
2117
2118 if (ANIM_animdata_get_context(C, &ac) == 0) {
2119 return;
2120 }
2121
2122 common_draw_status_header(C, gso, "Butterworth Smooth");
2123
2124 tBtwOperatorData *operator_data = (tBtwOperatorData *)gso->operator_data;
2125
2126 const float frame_rate = float(ac.scene->r.frs_sec) / ac.scene->r.frs_sec_base;
2127 const int samples_per_frame = RNA_int_get(op->ptr, "samples_per_frame");
2128 const float sampling_frequency = frame_rate * samples_per_frame;
2129
2130 const float cutoff_frequency = slider_factor_get_and_remember(op);
2131 const int blend_in_out = RNA_int_get(op->ptr, "blend_in_out");
2132
2134 cutoff_frequency, sampling_frequency, operator_data->coefficients);
2135
2136 LISTBASE_FOREACH (tFCurveSegmentLink *, segment, &operator_data->segment_links) {
2138 segment->segment,
2139 segment->samples,
2140 segment->sample_count,
2141 1,
2142 blend_in_out,
2143 samples_per_frame,
2144 operator_data->coefficients);
2145 }
2146
2147 LISTBASE_FOREACH (bAnimListElem *, ale, &operator_data->anim_data) {
2148 ale->update |= ANIM_UPDATE_DEFAULT;
2149 }
2150
2151 ANIM_animdata_update(&ac, &operator_data->anim_data);
2153}
2154
2155static int btw_smooth_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2156{
2157 const int invoke_result = graph_slider_invoke(C, op, event);
2158
2159 if (invoke_result == OPERATOR_CANCELLED) {
2160 return invoke_result;
2161 }
2162
2163 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
2165 gso->factor_prop = RNA_struct_find_property(op->ptr, "cutoff_frequency");
2166
2167 const int filter_order = RNA_int_get(op->ptr, "filter_order");
2168 const int samples_per_frame = RNA_int_get(op->ptr, "samples_per_frame");
2169
2170 btw_smooth_allocate_operator_data(gso, filter_order, samples_per_frame);
2172
2173 const float frame_rate = float(gso->scene->r.frs_sec) / gso->scene->r.frs_sec_base;
2174 const float sampling_frequency = frame_rate * samples_per_frame;
2175 ED_slider_factor_bounds_set(gso->slider, 0, sampling_frequency / 2);
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");
2180 common_draw_status_header(C, gso, "Butterworth Smooth");
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 = static_cast<float *>(
2214 MEM_callocN(sizeof(float) * sample_count, "Smooth FCurve Op Samples"));
2216 fcu, left_bezt.vec[1][0] - filter_order, samples_per_frame, samples, sample_count);
2218 fcu, segment, samples, sample_count, factor, blend_in_out, samples_per_frame, bw_coeff);
2219 MEM_freeN(samples);
2220 }
2221
2222 BLI_freelistN(&segments);
2223 ale->update |= ANIM_UPDATE_DEFAULT;
2224 }
2225
2227 ANIM_animdata_update(ac, &anim_data);
2228 ANIM_animdata_freelist(&anim_data);
2229}
2230
2232{
2233 bAnimContext ac;
2234
2235 if (ANIM_animdata_get_context(C, &ac) == 0) {
2236 return OPERATOR_CANCELLED;
2237 }
2238 const float blend = RNA_float_get(op->ptr, "blend");
2239 const float cutoff_frequency = RNA_float_get(op->ptr, "cutoff_frequency");
2240 const int filter_order = RNA_int_get(op->ptr, "filter_order");
2241 const int samples_per_frame = RNA_int_get(op->ptr, "samples_per_frame");
2242 const int blend_in_out = RNA_int_get(op->ptr, "blend_in_out");
2244 &ac, blend, blend_in_out, cutoff_frequency, filter_order, samples_per_frame);
2245
2246 /* Set notifier that keyframes have changed. */
2248
2249 return OPERATOR_FINISHED;
2250}
2251
2253{
2254 /* Identifiers. */
2255 ot->name = "Butterworth Smooth";
2256 ot->idname = "GRAPH_OT_butterworth_smooth";
2257 ot->description = "Smooth an F-Curve while maintaining the general shape of the curve";
2258
2259 /* API callbacks. */
2264
2265 /* Flags. */
2267
2269 "cutoff_frequency",
2270 3.0f,
2271 0.0f,
2272 FLT_MAX,
2273 "Frequency Cutoff (Hz)",
2274 "Lower values give a smoother curve",
2275 0.0f,
2276 FLT_MAX);
2277
2279 "filter_order",
2280 4,
2281 1,
2282 32,
2283 "Filter Order",
2284 "Higher values produce a harder frequency cutoff",
2285 1,
2286 16);
2287
2289 "samples_per_frame",
2290 1,
2291 1,
2292 64,
2293 "Samples per Frame",
2294 "How many samples to calculate per frame, helps with subframe data",
2295 1,
2296 16);
2297
2299 "blend",
2300 1.0f,
2301 0,
2302 FLT_MAX,
2303 "Blend",
2304 "How much to blend to the smoothed curve",
2305 0.0f,
2306 1.0f);
2307
2309 "blend_in_out",
2310 1,
2311 0,
2312 INT_MAX,
2313 "Blend In/Out",
2314 "Linearly blend the smooth data to the border frames of the selection",
2315 0,
2316 128);
2317}
2320/* -------------------------------------------------------------------- */
2324static void push_pull_graph_keys(bAnimContext *ac, const float factor)
2325{
2327}
2328
2330{
2331 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
2332
2333 common_draw_status_header(C, gso, "Push Pull Keys");
2334
2335 /* Reset keyframes to the state at invoke. */
2336 reset_bezts(gso);
2337 const float factor = slider_factor_get_and_remember(op);
2338 push_pull_graph_keys(&gso->ac, factor);
2340}
2341
2342static int push_pull_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2343{
2344 const int invoke_result = graph_slider_invoke(C, op, event);
2345
2346 if (invoke_result == OPERATOR_CANCELLED) {
2347 return invoke_result;
2348 }
2349
2350 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
2352 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
2355 common_draw_status_header(C, gso, "Push Pull Keys");
2356
2357 return invoke_result;
2358}
2359
2361{
2362 bAnimContext ac;
2363
2364 /* Get editor data. */
2365 if (ANIM_animdata_get_context(C, &ac) == 0) {
2366 return OPERATOR_CANCELLED;
2367 }
2368
2369 const float factor = RNA_float_get(op->ptr, "factor");
2370
2371 push_pull_graph_keys(&ac, factor);
2372
2373 /* Set notifier that keyframes have changed. */
2375
2376 return OPERATOR_FINISHED;
2377}
2378
2380{
2381 /* Identifiers. */
2382 ot->name = "Push Pull Keyframes";
2383 ot->idname = "GRAPH_OT_push_pull";
2384 ot->description = "Exaggerate or minimize the value of the selected keys";
2385
2386 /* API callbacks. */
2391
2392 /* Flags. */
2394
2396 "factor",
2397 1.0f,
2398 -FLT_MAX,
2399 FLT_MAX,
2400 "Factor",
2401 "Control how far to push or pull the keys",
2402 0.0f,
2403 2.0f);
2404}
2407/* -------------------------------------------------------------------- */
2412 {int(FCurveSegmentAnchor::LEFT), "LEFT", 0, "From Left", ""},
2413 {int(FCurveSegmentAnchor::RIGHT), "RIGHT", 0, "From Right", ""},
2414 {0, nullptr, 0, nullptr, nullptr},
2415};
2416
2418 const float factor,
2419 const FCurveSegmentAnchor anchor)
2420{
2421 ListBase anim_data = {nullptr, nullptr};
2422
2424 ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, eAnimCont_Types(ac->datatype));
2425 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
2426 FCurve *fcu = (FCurve *)ale->key_data;
2427 ListBase segments = find_fcurve_segments(fcu);
2428
2429 LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
2430 scale_from_fcurve_segment_neighbor(fcu, segment, factor, anchor);
2431 }
2432
2433 ale->update |= ANIM_UPDATE_DEFAULT;
2434 BLI_freelistN(&segments);
2435 }
2436
2437 ANIM_animdata_update(ac, &anim_data);
2438 ANIM_animdata_freelist(&anim_data);
2439}
2440
2442{
2443 char status_str[UI_MAX_DRAW_STR];
2444 char mode_str[32];
2445 char slider_string[UI_MAX_DRAW_STR];
2446
2447 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
2449
2450 /* Operator specific functionality that extends beyond the slider. */
2451 char op_slider_string[UI_MAX_DRAW_STR];
2452 const FCurveSegmentAnchor anchor = FCurveSegmentAnchor(RNA_enum_get(op->ptr, "anchor"));
2453 switch (anchor) {
2455 SNPRINTF(op_slider_string, "%s | %s", slider_string, IFACE_("[D] - Scale From Right End"));
2456 break;
2457
2459 SNPRINTF(op_slider_string, "%s | %s", slider_string, IFACE_("[D] - Scale From Left End"));
2460 break;
2461 }
2462
2463 STRNCPY(mode_str, IFACE_("Scale from Neighbor Keys"));
2464
2465 if (hasNumInput(&gso->num)) {
2466 char str_ofs[NUM_STR_REP_LEN];
2467
2468 outputNumInput(&gso->num, str_ofs, &gso->scene->unit);
2469
2470 SNPRINTF(status_str, "%s: %s", mode_str, str_ofs);
2471 }
2472 else {
2473 SNPRINTF(status_str, "%s: %s", mode_str, op_slider_string);
2474 }
2475
2476 ED_workspace_status_text(C, status_str);
2477}
2478
2480{
2481 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
2482
2484
2485 /* Reset keyframes to the state at invoke. */
2486 reset_bezts(gso);
2487 const float factor = slider_factor_get_and_remember(op);
2488 const FCurveSegmentAnchor anchor = FCurveSegmentAnchor(RNA_enum_get(op->ptr, "anchor"));
2489 scale_from_neighbor_graph_keys(&gso->ac, factor, anchor);
2491}
2492
2493static int scale_from_neighbor_modal(bContext *C, wmOperator *op, const wmEvent *event)
2494{
2495 if (event->val != KM_PRESS) {
2496 return graph_slider_modal(C, op, event);
2497 }
2498
2499 switch (event->type) {
2500 case EVT_DKEY: {
2502 switch (anchor) {
2504 RNA_enum_set(op->ptr, "anchor", int(FCurveSegmentAnchor::RIGHT));
2505 break;
2506
2508 RNA_enum_set(op->ptr, "anchor", int(FCurveSegmentAnchor::LEFT));
2509 break;
2510 }
2512 break;
2513 }
2514
2515 default:
2516 return graph_slider_modal(C, op, event);
2517 }
2519}
2520
2522{
2523 const int invoke_result = graph_slider_invoke(C, op, event);
2524
2525 if (invoke_result == OPERATOR_CANCELLED) {
2526 return OPERATOR_CANCELLED;
2527 }
2528
2529 tGraphSliderOp *gso = static_cast<tGraphSliderOp *>(op->customdata);
2531 gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
2534 ED_slider_factor_set(gso->slider, 1.0f);
2535
2536 return invoke_result;
2537}
2538
2540{
2541 bAnimContext ac;
2542
2543 /* Get editor data. */
2544 if (ANIM_animdata_get_context(C, &ac) == 0) {
2545 return OPERATOR_CANCELLED;
2546 }
2547
2548 const float factor = RNA_float_get(op->ptr, "factor");
2549
2550 const FCurveSegmentAnchor anchor = FCurveSegmentAnchor(RNA_enum_get(op->ptr, "anchor"));
2551 scale_from_neighbor_graph_keys(&ac, factor, anchor);
2552
2553 /* Set notifier that keyframes have changed. */
2555
2556 return OPERATOR_FINISHED;
2557}
2558
2560{
2561 /* Identifiers. */
2562 ot->name = "Scale from Neighbor";
2563 ot->idname = "GRAPH_OT_scale_from_neighbor";
2564 ot->description =
2565 "Increase or decrease the value of selected keys in relationship to the neighboring one";
2566
2567 /* API callbacks. */
2572
2573 /* Flags. */
2575
2577 "factor",
2578 0.0f,
2579 -FLT_MAX,
2580 FLT_MAX,
2581 "Factor",
2582 "The factor to scale keys with",
2583 -1.0f,
2584 1.0f);
2585
2587 "anchor",
2590 "Reference Key",
2591 "Which end of the segment to use as a reference to scale from");
2592}
2593
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)
#define LISTBASE_FOREACH(type, var, list)
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
MINLINE float min_ff(float a, float b)
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
#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:1044
struct BezTriple BezTriple
@ 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
void outputNumInput(NumInput *n, char *str, const UnitSettings *unit_settings)
Definition numinput.cc:88
bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
Definition numinput.cc:312
bool applyNumInput(NumInput *n, float *vec)
Definition numinput.cc:190
bool hasNumInput(const NumInput *n)
Definition numinput.cc:171
void ED_area_status_text(ScrArea *area, const char *str)
Definition area.cc:803
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:966
void ED_slider_status_string_get(const tSlider *slider, char *status_string, size_t size_of_status_string)
Definition ed_draw.cc:505
void ED_slider_init(tSlider *slider, const wmEvent *event)
Definition ed_draw.cc:467
void ED_slider_allow_overshoot_set(tSlider *slider, bool lower, bool upper)
Definition ed_draw.cc:580
@ SLIDER_MODE_PERCENT
Definition ED_util.hh:70
@ SLIDER_MODE_FLOAT
Definition ED_util.hh:70
SliderMode ED_slider_mode_get(const tSlider *slider)
Definition ed_draw.cc:609
void ED_slider_unit_set(tSlider *slider, const char *unit)
Definition ed_draw.cc:614
void ED_slider_mode_set(tSlider *slider, SliderMode mode)
Definition ed_draw.cc:604
void ED_slider_property_label_set(tSlider *slider, const char *property_label)
Definition ed_draw.cc:619
void ED_slider_destroy(bContext *C, tSlider *slider)
Definition ed_draw.cc:553
tSlider * ED_slider_create(bContext *C)
Definition ed_draw.cc:427
bool ED_slider_modal(tSlider *slider, const wmEvent *event)
Definition ed_draw.cc:472
void ED_slider_factor_bounds_set(tSlider *slider, float factor_bound_lower, float factor_bound_upper)
Definition ed_draw.cc:596
float ED_slider_factor_get(const tSlider *slider)
Definition ed_draw.cc:566
void ED_slider_factor_set(tSlider *slider, float factor)
Definition ed_draw.cc:571
Read Guarded memory(de)allocation.
#define UI_MAX_DRAW_STR
@ OPTYPE_BLOCKING
Definition WM_types.hh:164
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
@ OPTYPE_GRAB_CURSOR_X
Definition WM_types.hh:170
#define NC_ANIMATION
Definition WM_types.hh:355
@ KM_PRESS
Definition WM_types.hh:284
#define NA_EDITED
Definition WM_types.hh:550
#define ND_KEYFRAME
Definition WM_types.hh:461
void ANIM_animdata_freelist(ListBase *anim_data)
Definition anim_deps.cc:457
void ANIM_animdata_update(bAnimContext *ac, ListBase *anim_data)
Definition anim_deps.cc:350
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)
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
bool graphop_editable_keyframes_poll(bContext *C)
static int blend_to_ease_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void blend_offset_modal_update(bContext *C, wmOperator *op)
static const EnumPropertyItem scale_anchor_items[]
static int blend_to_neighbor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int match_slope_exec(bContext *C, wmOperator *op)
static int shear_modal(bContext *C, wmOperator *op, const wmEvent *event)
static int decimate_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 int blend_to_default_exec(bContext *C, wmOperator *op)
static bool decimate_poll_property(const bContext *, wmOperator *op, const PropertyRNA *prop)
static void reset_bezts(tGraphSliderOp *gso)
static void blend_to_ease_modal_update(bContext *C, wmOperator *op)
static int blend_to_ease_exec(bContext *C, wmOperator *op)
static void btw_smooth_allocate_operator_data(tGraphSliderOp *gso, const int filter_order, const int samples_per_frame)
static int gaussian_smooth_invoke(bContext *C, wmOperator *op, const wmEvent *event)
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)
static int btw_smooth_exec(bContext *C, wmOperator *op)
void GRAPH_OT_blend_to_neighbor(wmOperatorType *ot)
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 int breakdown_exec(bContext *C, wmOperator *op)
static void scale_from_neighbor_draw_status_header(bContext *C, wmOperator *op)
static int push_pull_invoke(bContext *C, wmOperator *op, const wmEvent *event)
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 int time_offset_exec(bContext *C, wmOperator *op)
static int graph_slider_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void push_pull_modal_update(bContext *C, wmOperator *op)
static int gaussian_smooth_exec(bContext *C, wmOperator *op)
static void shear_modal_update(bContext *C, wmOperator *op)
static int decimate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void scale_average_graph_keys(bAnimContext *ac, const float factor)
@ DECIM_RATIO
@ DECIM_ERROR
static int scale_average_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int blend_to_default_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static const EnumPropertyItem shear_direction_items[]
static void btw_smooth_modal_update(bContext *C, wmOperator *op)
static int time_offset_invoke(bContext *C, wmOperator *op, const wmEvent *event)
#define OPERATOR_DATA_FILTER
static int shear_exec(bContext *C, wmOperator *op)
static int push_pull_exec(bContext *C, wmOperator *op)
void GRAPH_OT_gaussian_smooth(wmOperatorType *ot)
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 void scale_average_modal_update(bContext *C, wmOperator *op)
static int ease_modal(bContext *C, wmOperator *op, const wmEvent *event)
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 void common_draw_status_header(bContext *C, tGraphSliderOp *gso, const char *operator_name)
static float slider_factor_get_and_remember(wmOperator *op)
static int scale_average_exec(bContext *C, wmOperator *op)
static void blend_to_default_graph_keys(bAnimContext *ac, const float factor)
static void match_slope_draw_status_header(bContext *C, tGraphSliderOp *gso)
static void time_offset_modal_update(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 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 int shear_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void blend_to_neighbor_modal_update(bContext *C, wmOperator *op)
void GRAPH_OT_scale_average(wmOperatorType *ot)
static int blend_to_neighbor_exec(bContext *C, wmOperator *op)
static int btw_smooth_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int blend_offset_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 int match_slope_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void decimate_graph_keys(bAnimContext *ac, float factor, float error_sq_max)
static int ease_exec(bContext *C, wmOperator *op)
static int scale_from_neighbor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int graph_slider_invoke(bContext *C, wmOperator *op, const wmEvent *event)
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 void blend_offset_draw_status_header(bContext *C, tGraphSliderOp *gso)
static void blend_to_ease_graph_keys(bAnimContext *ac, const float factor)
static int breakdown_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void GRAPH_OT_scale_from_neighbor(wmOperatorType *ot)
void GRAPH_OT_shear(wmOperatorType *ot)
void GRAPH_OT_blend_to_default(wmOperatorType *ot)
static void graph_slider_exit(bContext *C, wmOperator *op)
static void push_pull_graph_keys(bAnimContext *ac, const float factor)
static int scale_from_neighbor_modal(bContext *C, wmOperator *op, const wmEvent *event)
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 void btw_smooth_free_operator_data(void *operator_data)
static void time_offset_graph_keys(bAnimContext *ac, const float factor)
static int ease_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void scale_from_neighbor_graph_keys(bAnimContext *ac, const float factor, const FCurveSegmentAnchor anchor)
static void shear_draw_status_header(bContext *C, tGraphSliderOp *gso)
static int scale_from_neighbor_exec(bContext *C, wmOperator *op)
static int blend_offset_exec(bContext *C, wmOperator *op)
static void gaussian_smooth_graph_keys(bAnimContext *ac, const float factor, double *kernel, const int filter_width)
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)
void smooth_fcurve_segment(FCurve *fcu, FCurveSegment *segment, float *samples, const float factor, const int kernel_size, double *kernel)
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:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
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)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
int RNA_int_get(PointerRNA *ptr, const char *name)
float RNA_float_get(PointerRNA *ptr, const char *name)
const char * RNA_property_ui_name(const PropertyRNA *prop)
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
bAnimListElem * next
ButterworthCoefficients * coefficients
PropertyRNA * factor_prop
void(* modal_update)(bContext *, wmOperator *)
void(* free_operator_data)(void *operator_data)
short val
Definition WM_types.hh:724
short type
Definition WM_types.hh:722
const char * name
Definition WM_types.hh:990
bool(* poll_property)(const bContext *C, wmOperator *op, const PropertyRNA *prop) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1048
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
std::string(* get_description)(bContext *C, wmOperatorType *ot, PointerRNA *ptr)
Definition WM_types.hh:1074
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
struct PointerRNA * ptr
void WM_cursor_modal_set(wmWindow *win, int val)
void WM_cursor_modal_restore(wmWindow *win)
@ WM_CURSOR_EW_SCROLL
Definition wm_cursors.hh:53
void WM_report(eReportType type, const char *message)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ RIGHTMOUSE
@ EVT_TABKEY
@ EVT_PADENTER
@ MOUSEMOVE
@ LEFTMOUSE
@ EVT_ESCKEY
@ EVT_DKEY
@ EVT_RETKEY
PointerRNA * ptr
Definition wm_files.cc:4126
wmOperatorType * ot
Definition wm_files.cc:4125