Blender V4.3
graph_edit.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11#include <cfloat>
12#include <cmath>
13#include <cstdlib>
14#include <cstring>
15
16#ifdef WITH_AUDASPACE
17# include <AUD_Special.h>
18#endif
19
20#include "MEM_guardedalloc.h"
21
22#include "BLI_blenlib.h"
23#include "BLI_math_rotation.h"
24#include "BLI_utildefines.h"
25
26#include "DNA_anim_types.h"
27#include "DNA_scene_types.h"
28
29#include "RNA_access.hh"
30#include "RNA_define.hh"
31#include "RNA_enum_types.hh"
32#include "RNA_prototypes.hh"
33
34#include "BLT_translation.hh"
35
36#include "BKE_animsys.h"
37#include "BKE_context.hh"
38#include "BKE_fcurve.hh"
39#include "BKE_global.hh"
40#include "BKE_nla.hh"
41#include "BKE_report.hh"
42#include "BKE_scene.hh"
43
45
46#include "UI_interface_icons.hh"
47#include "UI_view2d.hh"
48
49#include "ANIM_animdata.hh"
50#include "ANIM_fcurve.hh"
51#include "ANIM_keyframing.hh"
52#include "ED_anim_api.hh"
53#include "ED_keyframes_edit.hh"
54#include "ED_keyframing.hh"
55#include "ED_markers.hh"
56#include "ED_screen.hh"
57#include "ED_transform.hh"
58
59#include "WM_api.hh"
60#include "WM_types.hh"
61
62#include "graph_intern.hh"
63
64/* -------------------------------------------------------------------- */
68/* Mode defines for insert keyframes tool. */
75
76/* RNA mode types for insert keyframes tool. */
79 "ALL",
80 0,
81 "All Channels",
82 "Insert a keyframe on all visible and editable F-Curves using each curve's current value"},
84 "SEL",
85 0,
86 "Only Selected Channels",
87 "Insert a keyframe on selected F-Curves using each curve's current value"},
89 "ACTIVE",
90 0,
91 "Only Active F-Curve",
92 "Insert a keyframe on the active F-Curve using the curve's current value"},
94 "CURSOR_ACTIVE",
95 0,
96 "Active Channels at Cursor",
97 "Insert a keyframe for the active F-Curve at the cursor point"},
99 "CURSOR_SEL",
100 0,
101 "Selected Channels at Cursor",
102 "Insert a keyframe for selected F-Curves at the cursor point"},
103 {0, nullptr, 0, nullptr, nullptr},
104};
105
106/* This function is responsible for snapping keyframes to frame-times. */
108{
109 using namespace blender::animrig;
110 ListBase anim_data = {nullptr, nullptr};
111 int filter;
112 size_t num_items;
113
114 ReportList *reports = ac->reports;
115 SpaceGraph *sipo = (SpaceGraph *)ac->sl;
116 Scene *scene = ac->scene;
117 ToolSettings *ts = scene->toolsettings;
118
119 /* Filter data. */
122 if (mode & GRAPHKEYS_INSERTKEY_SEL) {
123 filter |= ANIMFILTER_SEL;
124 }
125 else if (mode & GRAPHKEYS_INSERTKEY_ACTIVE) {
126 filter |= ANIMFILTER_ACTIVE;
127 }
128
129 num_items = ANIM_animdata_filter(
130 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
131 if (num_items == 0) {
132 if (mode & GRAPHKEYS_INSERTKEY_ACTIVE) {
133 BKE_report(reports,
134 RPT_ERROR,
135 "No active F-Curve to add a keyframe to. Select an editable F-Curve first");
136 }
137 else if (mode & GRAPHKEYS_INSERTKEY_SEL) {
138 BKE_report(reports, RPT_ERROR, "No selected F-Curves to add keyframes to");
139 }
140 else {
141 BKE_report(reports, RPT_ERROR, "No channels to add keyframes to");
142 }
143
144 return;
145 }
146
147 /* Init key-framing flag. */
149 KeyframeSettings settings = get_keyframe_settings(true);
150 settings.keyframe_type = eBezTriple_KeyframeType(ts->keyframe_type);
151
152 /* Insert keyframes. */
153 if (mode & GRAPHKEYS_INSERTKEY_CURSOR) {
154 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
155 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
156 FCurve *fcu = (FCurve *)ale->key_data;
157
158 short mapping_flag = ANIM_get_normalization_flags(ac->sl);
159 float offset;
160 float unit_scale = ANIM_unit_mapping_get_factor(
161 ac->scene, ale->id, static_cast<FCurve *>(ale->key_data), mapping_flag, &offset);
162
163 float x, y;
164
165 /* perform time remapping for x-coordinate (if necessary) */
166 if ((sipo) && (sipo->mode == SIPO_MODE_DRIVERS)) {
167 x = sipo->cursorTime;
168 }
169 else if (adt) {
170 x = BKE_nla_tweakedit_remap(adt, float(scene->r.cfra), NLATIME_CONVERT_UNMAP);
171 }
172 else {
173 x = float(scene->r.cfra);
174 }
175
176 /* Normalize units of cursor's value. */
177 if (sipo) {
178 y = (sipo->cursorVal / unit_scale) - offset;
179 }
180 else {
181 y = 0.0f;
182 }
183
184 /* Insert keyframe directly into the F-Curve. */
185 insert_vert_fcurve(fcu, {x, y}, settings, eInsertKeyFlags(0));
186
187 ale->update |= ANIM_UPDATE_DEFAULT;
188 }
189 }
190 else {
192 ac->depsgraph, float(scene->r.cfra));
193 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
194 FCurve *fcu = (FCurve *)ale->key_data;
195
196 /* Read value from property the F-Curve represents, or from the curve only?
197 *
198 * - ale->id != nullptr:
199 * Typically, this means that we have enough info to try resolving the path.
200 * - ale->owner != nullptr:
201 * If this is set, then the path may not be resolvable from the ID alone,
202 * so it's easier for now to just read the F-Curve directly.
203 * (TODO: add the full-blown PointerRNA relative parsing case here... (Joshua Leung 2015))
204 * - fcu->driver != nullptr:
205 * If this is set, then it's a driver. If we don't check for this, we'd end
206 * up adding the keyframes on a new F-Curve in the action data instead.
207 */
208 const std::optional<blender::StringRefNull> channel_group = fcu->grp ? std::optional(
209 fcu->grp->name) :
210 std::nullopt;
211 if (ale->id && !ale->owner && !fcu->driver) {
212 PointerRNA id_rna_pointer = RNA_id_pointer_create(ale->id);
213 CombinedKeyingResult result = insert_keyframes(ac->bmain,
214 &id_rna_pointer,
215 channel_group,
216 {{fcu->rna_path, {}, fcu->array_index}},
217 std::nullopt,
218 anim_eval_context,
220 flag);
221 if (result.get_count(SingleKeyingResult::SUCCESS) == 0) {
222 result.generate_reports(reports);
223 }
224 }
225 else {
226 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
227
228 /* Adjust current frame for NLA-mapping. */
229 float cfra = float(scene->r.cfra);
230 if ((sipo) && (sipo->mode == SIPO_MODE_DRIVERS)) {
231 cfra = sipo->cursorTime;
232 }
233 else if (adt) {
234 cfra = BKE_nla_tweakedit_remap(adt, float(scene->r.cfra), NLATIME_CONVERT_UNMAP);
235 }
236
237 const float curval = evaluate_fcurve_only_curve(fcu, cfra);
238 insert_vert_fcurve(fcu, {cfra, curval}, settings, eInsertKeyFlags(0));
239 }
240
241 ale->update |= ANIM_UPDATE_DEFAULT;
242 }
243 }
244
245 ANIM_animdata_update(ac, &anim_data);
246 ANIM_animdata_freelist(&anim_data);
247}
248
249/* ------------------- */
250
252{
253 bAnimContext ac;
255
256 /* Get editor data. */
257 if (ANIM_animdata_get_context(C, &ac) == 0) {
258 return OPERATOR_CANCELLED;
259 }
260
262
263 /* Which channels to affect? */
264 mode = eGraphKeys_InsertKey_Types(RNA_enum_get(op->ptr, "type"));
265
266 /* Insert keyframes. */
267 insert_graph_keys(&ac, mode);
268
269 /* Set notifier that keyframes have changed. */
271
272 return OPERATOR_FINISHED;
273}
274
276{
277 /* Identifiers */
278 ot->name = "Insert Keyframes";
279 ot->idname = "GRAPH_OT_keyframe_insert";
280 ot->description = "Insert keyframes for the specified channels";
281
282 /* API callbacks */
286
287 /* Flags */
289
290 /* Id-props */
291 ot->prop = RNA_def_enum(ot->srna, "type", prop_graphkeys_insertkey_types, 0, "Type", "");
292}
293
296/* -------------------------------------------------------------------- */
301{
302 using namespace blender::animrig;
303 bAnimContext ac;
304 bAnimListElem *ale;
305 AnimData *adt;
306 FCurve *fcu;
307 float frame, val;
308
309 /* Get animation context. */
310 if (ANIM_animdata_get_context(C, &ac) == 0) {
311 return OPERATOR_CANCELLED;
312 }
313
314 /* Get active F-Curve 'anim-list-element'. */
315 ale = get_active_fcurve_channel(&ac);
316 if (ELEM(nullptr, ale, ale->data)) {
317 if (ale) {
318 MEM_freeN(ale);
319 }
320 return OPERATOR_CANCELLED;
321 }
322 fcu = static_cast<FCurve *>(ale->data);
323
324 /* When there are F-Modifiers on the curve, only allow adding
325 * keyframes if these will be visible after doing so...
326 */
327 if (BKE_fcurve_is_keyframable(fcu)) {
328 ListBase anim_data;
330
331 short mapping_flag = ANIM_get_normalization_flags(ac.sl);
332 float scale, offset;
333
334 /* Preserve selection? */
335 if (RNA_boolean_get(op->ptr, "extend") == false) {
336 /* Deselect all keyframes first,
337 * so that we can immediately start manipulating the newly added one(s)
338 * - only affect the keyframes themselves, as we don't want channels popping in and out. */
339 deselect_graph_keys(&ac, false, SELECT_SUBTRACT, false);
340 }
341
342 /* Get frame and value from props. */
343 frame = RNA_float_get(op->ptr, "frame");
344 val = RNA_float_get(op->ptr, "value");
345
346 /* Apply inverse NLA-mapping to frame to get correct time in un-scaled action. */
347 adt = ANIM_nla_mapping_get(&ac, ale);
349
350 /* Apply inverse unit-mapping to value to get correct value for F-Curves. */
352 ac.scene, ale->id, fcu, mapping_flag | ANIM_UNITCONV_RESTORE, &offset);
353
354 val = val * scale - offset;
355
356 KeyframeSettings settings = get_keyframe_settings(true);
357 settings.keyframe_type = eBezTriple_KeyframeType(ts->keyframe_type);
358
359 /* Insert keyframe on the specified frame + value. */
360 insert_vert_fcurve(fcu, {frame, val}, settings, eInsertKeyFlags(0));
361
362 ale->update |= ANIM_UPDATE_DEPS;
363
364 BLI_listbase_clear(&anim_data);
365 BLI_addtail(&anim_data, ale);
366
367 ANIM_animdata_update(&ac, &anim_data);
368 }
369 else {
370 /* Warn about why this can't happen. */
371 if (fcu->fpt) {
372 BKE_report(op->reports, RPT_ERROR, "Keyframes cannot be added to sampled F-Curves");
373 }
374 else if (fcu->flag & FCURVE_PROTECTED) {
375 BKE_report(op->reports, RPT_ERROR, "Active F-Curve is not editable");
376 }
377 else {
378 BKE_report(op->reports, RPT_ERROR, "Remove F-Modifiers from F-Curve to add keyframes");
379 }
380 }
381
382 /* Free temp data. */
383 MEM_freeN(ale);
384
385 /* Set notifier that keyframes have changed. */
387
388 /* Done */
389 return OPERATOR_FINISHED;
390}
391
393{
394 bAnimContext ac;
395 ARegion *region;
396 View2D *v2d;
397 int mval[2];
398 float x, y;
399
400 /* Get animation context. */
401 if (ANIM_animdata_get_context(C, &ac) == 0) {
402 return OPERATOR_CANCELLED;
403 }
404
405 /* Store mouse coordinates in View2D space, into the operator's properties. */
406 region = ac.region;
407 v2d = &region->v2d;
408
409 mval[0] = (event->xy[0] - region->winrct.xmin);
410 mval[1] = (event->xy[1] - region->winrct.ymin);
411
412 UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y);
413
414 RNA_float_set(op->ptr, "frame", x);
415 RNA_float_set(op->ptr, "value", y);
416
417 /* Run exec now. */
418 return graphkeys_click_insert_exec(C, op);
419}
420
422{
423 PropertyRNA *prop;
424
425 /* Identifiers */
426 ot->name = "Click-Insert Keyframes";
427 ot->idname = "GRAPH_OT_click_insert";
428 ot->description = "Insert new keyframe at the cursor position for the active F-Curve";
429
430 /* API callbacks */
434
435 /* Flags */
437
438 /* Properties */
440 "frame",
441 1.0f,
442 -FLT_MAX,
443 FLT_MAX,
444 "Frame Number",
445 "Frame to insert keyframe on",
446 0,
447 100);
449 ot->srna, "value", 1.0f, -FLT_MAX, FLT_MAX, "Value", "Value for keyframe on", 0, 100);
450
451 prop = RNA_def_boolean(ot->srna,
452 "extend",
453 false,
454 "Extend",
455 "Extend selection instead of deselecting everything first");
457}
458
461/* -------------------------------------------------------------------- */
468{
469 ListBase anim_data = {nullptr, nullptr};
470 int filter, ok = 0;
471
472 /* Clear buffer first. */
474
475 /* Filter data
476 * - First time we try to filter more strictly, allowing only selected channels
477 * to allow copying animation between channels.
478 */
481
483 &anim_data,
485 ac->data,
486 eAnimCont_Types(ac->datatype)) == 0)
487 {
489 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
490 }
491
492 /* Copy keyframes. */
493 ok = copy_animedit_keys(ac, &anim_data);
494
495 /* Clean up. */
496 ANIM_animdata_freelist(&anim_data);
497
498 return ok;
499}
500
502 const eKeyPasteOffset offset_mode,
503 const eKeyPasteValueOffset value_offset_mode,
504 const eKeyMergeMode merge_mode,
505 bool flip)
506{
507 ListBase anim_data = {nullptr, nullptr};
508 int filter;
509
510 /* Filter data
511 * - First time we try to filter more strictly, allowing only selected channels
512 * to allow copying animation between channels
513 * - Second time, we loosen things up if nothing was found the first time, allowing
514 * users to just paste keyframes back into the original curve again #31670.
515 */
518
520 &anim_data,
522 ac->data,
523 eAnimCont_Types(ac->datatype)) == 0)
524 {
526 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
527 }
528
529 /* Paste keyframes. */
531 ac, &anim_data, offset_mode, value_offset_mode, merge_mode, flip);
532
533 /* Clean up. */
534 ANIM_animdata_freelist(&anim_data);
535
536 return ok;
537}
538
539/* ------------------- */
540
542{
543 bAnimContext ac;
544
545 /* Get editor data. */
546 if (ANIM_animdata_get_context(C, &ac) == 0) {
547 return OPERATOR_CANCELLED;
548 }
549
550 /* Copy keyframes. */
551 if (copy_graph_keys(&ac)) {
552 BKE_report(op->reports, RPT_ERROR, "No keyframes copied to the internal clipboard");
553 return OPERATOR_CANCELLED;
554 }
555
556 /* Just return - no operator needed here (no changes). */
557 return OPERATOR_FINISHED;
558}
559
561{
562 /* Identifiers */
563 ot->name = "Copy Keyframes";
564 ot->idname = "GRAPH_OT_copy";
565 ot->description = "Copy selected keyframes to the internal clipboard";
566
567 /* API callbacks */
570
571 /* Flags */
573}
574
576{
577 bAnimContext ac;
578
579 const eKeyPasteOffset offset_mode = eKeyPasteOffset(RNA_enum_get(op->ptr, "offset"));
580 const eKeyPasteValueOffset value_offset_mode = eKeyPasteValueOffset(
581 RNA_enum_get(op->ptr, "value_offset"));
582 const eKeyMergeMode merge_mode = eKeyMergeMode(RNA_enum_get(op->ptr, "merge"));
583 const bool flipped = RNA_boolean_get(op->ptr, "flipped");
584
585 /* Get editor data. */
586 if (ANIM_animdata_get_context(C, &ac) == 0) {
587 return OPERATOR_CANCELLED;
588 }
589
590 /* Ac.reports by default will be the global reports list, which won't show warnings. */
591 ac.reports = op->reports;
592
593 const eKeyPasteError kf_empty = paste_graph_keys(
594 &ac, offset_mode, value_offset_mode, merge_mode, flipped);
595 switch (kf_empty) {
597 break;
598
600 BKE_report(op->reports, RPT_ERROR, "No selected F-Curves to paste into");
601 return OPERATOR_CANCELLED;
602
604 BKE_report(op->reports, RPT_ERROR, "No data in the internal clipboard to paste");
605 return OPERATOR_CANCELLED;
606 }
607
608 /* Set notifier that keyframes have changed. */
610
611 return OPERATOR_FINISHED;
612}
613
614static std::string graphkeys_paste_get_description(bContext * /*C*/,
615 wmOperatorType * /*ot*/,
617{
618 /* Custom description if the 'flipped' option is used. */
619 if (RNA_boolean_get(ptr, "flipped")) {
620 return TIP_("Paste keyframes from mirrored bones if they exist");
621 }
622
623 /* Use the default description in the other cases. */
624 return "";
625}
626
628{
629 PropertyRNA *prop;
630
631 /* Identifiers */
632 ot->name = "Paste Keyframes";
633 ot->idname = "GRAPH_OT_paste";
634 ot->description =
635 "Paste keyframes from the internal clipboard for the selected channels, starting on the "
636 "current "
637 "frame";
638
639 /* API callbacks */
640
641 // ot->invoke = WM_operator_props_popup; /* better wait for graph redo panel */
645
646 /* Flags */
648
649 /* Props */
651 "offset",
654 "Frame Offset",
655 "Paste time offset of keys");
657 "value_offset",
660 "Value Offset",
661 "Paste keys with a value offset");
663 "merge",
666 "Type",
667 "Method of merging pasted keys and existing");
668 prop = RNA_def_boolean(
669 ot->srna, "flipped", false, "Flipped", "Paste keyframes from mirrored bones if they exist");
671}
672
675/* -------------------------------------------------------------------- */
680{
681 ListBase anim_data = {nullptr, nullptr};
682 int filter;
683 bool changed = false;
684
685 /* Filter data. */
689 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
690
691 /* Loop through filtered data and delete selected keys. */
692 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
693 changed |= duplicate_fcurve_keys((FCurve *)ale->key_data);
694
695 ale->update |= ANIM_UPDATE_DEFAULT;
696 }
697
698 ANIM_animdata_update(ac, &anim_data);
699 ANIM_animdata_freelist(&anim_data);
700
701 return changed;
702}
703
704/* ------------------- */
705
707{
708 bAnimContext ac;
709
710 /* Get editor data. */
711 if (ANIM_animdata_get_context(C, &ac) == 0) {
712 return OPERATOR_CANCELLED;
713 }
714
715 /* Duplicate keyframes. */
716 if (!duplicate_graph_keys(&ac)) {
717 return OPERATOR_CANCELLED;
718 }
719
720 /* Set notifier that keyframes have changed. */
722
723 return OPERATOR_FINISHED;
724}
725
727{
728 /* Identifiers */
729 ot->name = "Duplicate Keyframes";
730 ot->idname = "GRAPH_OT_duplicate";
731 ot->description = "Make a copy of all selected keyframes";
732
733 /* API callbacks */
736
737 /* Flags */
739
740 /* To give to transform. */
742}
743
746/* -------------------------------------------------------------------- */
751{
752 ListBase anim_data = {nullptr, nullptr};
753 int filter;
754 bool changed_final = false;
755
756 /* Filter data. */
760 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
761
762 /* Loop through filtered data and delete selected keys. */
763 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
764 FCurve *fcu = (FCurve *)ale->key_data;
765 AnimData *adt = ale->adt;
766 bool changed;
767
768 /* Delete selected keyframes only. */
769 changed = BKE_fcurve_delete_keys_selected(fcu);
770
771 if (changed) {
772 ale->update |= ANIM_UPDATE_DEFAULT;
773 changed_final = true;
774 }
775
776 /* Only delete curve too if it won't be doing anything anymore. */
777 if (BKE_fcurve_is_empty(fcu)) {
779 ale->key_data = nullptr;
780 }
781 }
782
783 ANIM_animdata_update(ac, &anim_data);
784 ANIM_animdata_freelist(&anim_data);
785
786 return changed_final;
787}
788
789/* ------------------- */
790
792{
793 bAnimContext ac;
794
795 /* Get editor data. */
796 if (ANIM_animdata_get_context(C, &ac) == 0) {
797 return OPERATOR_CANCELLED;
798 }
799
800 /* Delete keyframes. */
801 if (!delete_graph_keys(&ac)) {
802 return OPERATOR_CANCELLED;
803 }
804
805 /* Set notifier that keyframes have changed. */
807
808 return OPERATOR_FINISHED;
809}
810
811static int graphkeys_delete_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
812{
813 if (RNA_boolean_get(op->ptr, "confirm")) {
814 return WM_operator_confirm_ex(C,
815 op,
816 IFACE_("Delete selected keyframes?"),
817 nullptr,
818 IFACE_("Delete"),
820 false);
821 }
822 return graphkeys_delete_exec(C, op);
823}
824
826{
827 /* Identifiers */
828 ot->name = "Delete Keyframes";
829 ot->idname = "GRAPH_OT_delete";
830 ot->description = "Remove all selected keyframes";
831
832 /* API callbacks */
836
837 /* Flags */
840}
841
844/* -------------------------------------------------------------------- */
848static void clean_graph_keys(bAnimContext *ac, float thresh, bool clean_chan)
849{
850 ListBase anim_data = {nullptr, nullptr};
851 int filter;
852
853 /* Filter data. */
856 if (clean_chan) {
857 filter |= ANIMFILTER_SEL;
858 }
860 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
861
862 const bool only_selected_keys = !clean_chan;
863 /* Loop through filtered data and clean curves. */
864 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
865
866 clean_fcurve(ac, ale, thresh, clean_chan, only_selected_keys);
867
868 ale->update |= ANIM_UPDATE_DEFAULT;
869 }
870
871 ANIM_animdata_update(ac, &anim_data);
872 ANIM_animdata_freelist(&anim_data);
873}
874
875/* ------------------- */
876
878{
879 bAnimContext ac;
880 float thresh;
881 bool clean_chan;
882
883 /* Get editor data. */
884 if (ANIM_animdata_get_context(C, &ac) == 0) {
885 return OPERATOR_CANCELLED;
886 }
887
888 /* Get cleaning threshold. */
889 thresh = RNA_float_get(op->ptr, "threshold");
890 clean_chan = RNA_boolean_get(op->ptr, "channels");
891 /* Clean keyframes. */
892 clean_graph_keys(&ac, thresh, clean_chan);
893
894 /* Set notifier that keyframes have changed. */
896
897 return OPERATOR_FINISHED;
898}
899
901{
902 /* Identifiers */
903 ot->name = "Clean Keyframes";
904 ot->idname = "GRAPH_OT_clean";
905 ot->description = "Simplify F-Curves by removing closely spaced keyframes";
906
907 /* API callbacks */
908 // ot->invoke = ???; /* XXX we need that number popup for this! */
911
912 /* Flags */
914
915 /* Properties */
917 ot->srna, "threshold", 0.001f, 0.0f, FLT_MAX, "Threshold", "", 0.0f, 1000.0f);
918 RNA_def_boolean(ot->srna, "channels", false, "Channels", "");
919}
920
923/* -------------------------------------------------------------------- */
929/* Bake each F-Curve into a set of samples. */
930static void convert_keys_to_samples(bAnimContext *ac, int start, int end)
931{
932 ListBase anim_data = {nullptr, nullptr};
933 int filter;
934
935 /* Filter data. */
939 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
940
941 /* Loop through filtered data and add keys between selected keyframes on every frame. */
942 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
943 FCurve *fcu = (FCurve *)ale->key_data;
944 ChannelDriver *driver = fcu->driver;
945
946 /* Disable driver so that it don't muck up the sampling process. */
947 fcu->driver = nullptr;
948
949 /* Create samples. */
950 fcurve_store_samples(fcu, nullptr, start, end, fcurve_samplingcb_evalcurve);
951
952 /* Restore driver. */
953 fcu->driver = driver;
954
955 ale->update |= ANIM_UPDATE_DEPS;
956 }
957
958 ANIM_animdata_update(ac, &anim_data);
959 ANIM_animdata_freelist(&anim_data);
960}
961
962/* ------------------- */
963
965{
966 bAnimContext ac;
967 Scene *scene = nullptr;
968 int start, end;
969
970 /* Get editor data. */
971 if (ANIM_animdata_get_context(C, &ac) == 0) {
972 return OPERATOR_CANCELLED;
973 }
974
975 /* For now, init start/end from preview-range extents. */
976 /* TODO: add properties for this. (Joshua Leung 2009) */
977 scene = ac.scene;
978 start = PSFRA;
979 end = PEFRA;
980
981 /* Sample keyframes. */
982 convert_keys_to_samples(&ac, start, end);
983
984 /* Set notifier that keyframes have changed. */
985 /* NOTE: some distinction between order/number of keyframes and type should be made? */
987
988 return OPERATOR_FINISHED;
989}
990
992{
993 /* Identifiers */
994 ot->name = "Keys to Samples";
995 ot->idname = "GRAPH_OT_keys_to_samples";
996 ot->description =
997 "Convert selected channels to an uneditable set of samples to save storage space";
998
999 /* API callbacks */
1002
1003 /* Flags */
1005
1006 /* TODO: add props for start/end frames (Joshua Leung 2009) */
1007}
1008
1011/* -------------------------------------------------------------------- */
1017/* Convert F-Points into F-Curves. */
1018static void convert_samples_to_keys(bAnimContext *ac, int start, int end)
1019{
1020 ListBase anim_data = {nullptr, nullptr};
1021
1022 /* Filter data. */
1026 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
1027
1028 /* Loop through filtered data and add keys between selected keyframes on every frame. */
1029 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
1030 FCurve *fcu = (FCurve *)ale->key_data;
1031
1032 fcurve_samples_to_keyframes(fcu, start, end);
1033
1034 ale->update |= ANIM_UPDATE_DEPS;
1035 }
1036
1037 ANIM_animdata_update(ac, &anim_data);
1038 ANIM_animdata_freelist(&anim_data);
1039}
1040
1041/* ------------------- */
1042
1044{
1045 bAnimContext ac;
1046 Scene *scene = nullptr;
1047 int start, end;
1048
1049 /* Get editor data. */
1050 if (ANIM_animdata_get_context(C, &ac) == 0) {
1051 return OPERATOR_CANCELLED;
1052 }
1053
1054 scene = ac.scene;
1055 start = PSFRA;
1056 end = PEFRA;
1057
1058 convert_samples_to_keys(&ac, start, end);
1059
1060 /* Set notifier that keyframes have changed. */
1061 /* NOTE: some distinction between order/number of keyframes and type should be made? */
1063
1064 return OPERATOR_FINISHED;
1065}
1066
1068{
1069 /* Identifiers */
1070 ot->name = "Samples to Keys";
1071 ot->idname = "GRAPH_OT_samples_to_keys";
1072 ot->description = "Convert selected channels from samples to keyframes";
1073
1074 /* API callbacks */
1077
1078 /* Flags */
1080}
1081
1082#ifdef WITH_AUDASPACE
1083
1086/* -------------------------------------------------------------------- */
1092/* ------------------- */
1093
1094/* Custom data storage passed to the F-Sample-ing function,
1095 * which provides the necessary info for baking the sound.
1096 */
1097struct tSoundBakeInfo {
1098 float *samples;
1099 int length;
1100 int cfra;
1101};
1102
1103/* ------------------- */
1104
1105/* Sampling callback used to determine the value from the sound to
1106 * save in the F-Curve at the specified frame.
1107 */
1108static float fcurve_samplingcb_sound(FCurve * /*fcu*/, void *data, float evaltime)
1109{
1110 tSoundBakeInfo *sbi = (tSoundBakeInfo *)data;
1111
1112 int position = evaltime - sbi->cfra;
1113 if ((position < 0) || (position >= sbi->length)) {
1114 return 0.0f;
1115 }
1116
1117 return sbi->samples[position];
1118}
1119
1120/* ------------------- */
1121
1123{
1124 bAnimContext ac;
1125 ListBase anim_data = {nullptr, nullptr};
1126 int filter;
1127
1128 tSoundBakeInfo sbi;
1129 Scene *scene = nullptr;
1130 int start, end;
1131
1132 char filepath[FILE_MAX];
1133
1134 /* Get editor data. */
1135 if (ANIM_animdata_get_context(C, &ac) == 0) {
1136 return OPERATOR_CANCELLED;
1137 }
1138
1139 RNA_string_get(op->ptr, "filepath", filepath);
1140
1141 if (!BLI_is_file(filepath)) {
1142 BKE_reportf(op->reports, RPT_ERROR, "File not found '%s'", filepath);
1143 return OPERATOR_CANCELLED;
1144 }
1145
1146 scene = ac.scene; /* Current scene. */
1147
1148 /* Store necessary data for the baking steps. */
1149 sbi.samples = AUD_readSoundBuffer(filepath,
1150 RNA_float_get(op->ptr, "low"),
1151 RNA_float_get(op->ptr, "high"),
1152 RNA_float_get(op->ptr, "attack"),
1153 RNA_float_get(op->ptr, "release"),
1154 RNA_float_get(op->ptr, "threshold"),
1155 RNA_boolean_get(op->ptr, "use_accumulate"),
1156 RNA_boolean_get(op->ptr, "use_additive"),
1157 RNA_boolean_get(op->ptr, "use_square"),
1158 RNA_float_get(op->ptr, "sthreshold"),
1159 FPS,
1160 &sbi.length,
1161 0);
1162
1163 if (sbi.samples == nullptr) {
1164 BKE_report(op->reports, RPT_ERROR, "Unsupported audio format");
1165 return OPERATOR_CANCELLED;
1166 }
1167
1168 /* Determine extents of the baking. */
1169 sbi.cfra = start = scene->r.cfra;
1170 end = scene->r.cfra + sbi.length - 1;
1171
1172 /* Filter anim channels. */
1176 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
1177
1178 /* Loop through all selected F-Curves, replacing its data with the sound samples. */
1179 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
1180 FCurve *fcu = (FCurve *)ale->key_data;
1181
1182 /* Sample the sound. */
1183 fcurve_store_samples(fcu, &sbi, start, end, fcurve_samplingcb_sound);
1184
1185 ale->update |= ANIM_UPDATE_DEFAULT;
1186 }
1187
1188 /* Free sample data. */
1189 free(sbi.samples);
1190
1191 /* Validate keyframes after editing. */
1192 ANIM_animdata_update(&ac, &anim_data);
1193 ANIM_animdata_freelist(&anim_data);
1194
1195 /* Set notifier that 'keyframes' have changed. */
1197
1198 return OPERATOR_FINISHED;
1199}
1200
1201#else /* WITH_AUDASPACE */
1202
1204{
1205 BKE_report(op->reports, RPT_ERROR, "Compiled without sound support");
1206
1207 return OPERATOR_CANCELLED;
1208}
1209
1210#endif /* WITH_AUDASPACE */
1211
1213{
1214 bAnimContext ac;
1215
1216 /* Verify editor data. */
1217 if (ANIM_animdata_get_context(C, &ac) == 0) {
1218 return OPERATOR_CANCELLED;
1219 }
1220
1221 return WM_operator_filesel(C, op, event);
1222}
1223
1225{
1226 /* Identifiers */
1227 ot->name = "Sound to Samples";
1228 ot->idname = "GRAPH_OT_sound_to_samples";
1229 ot->description = "Bakes a sound wave to samples on selected channels";
1230
1231 /* API callbacks */
1235
1236 /* Flags */
1238
1239 /* Properties */
1248 "low",
1249 0.0f,
1250 0.0,
1251 100000.0,
1252 "Lowest Frequency",
1253 "Cutoff frequency of a high-pass filter that is applied to the audio data",
1254 0.1,
1255 1000.00);
1257 "high",
1258 100000.0,
1259 0.0,
1260 100000.0,
1261 "Highest Frequency",
1262 "Cutoff frequency of a low-pass filter that is applied to the audio data",
1263 0.1,
1264 1000.00);
1266 "attack",
1267 0.005,
1268 0.0,
1269 2.0,
1270 "Attack Time",
1271 "Value for the envelope calculation that tells how fast the envelope can rise "
1272 "(the lower the value the steeper it can rise)",
1273 0.01,
1274 0.1);
1276 "release",
1277 0.2,
1278 0.0,
1279 5.0,
1280 "Release Time",
1281 "Value for the envelope calculation that tells how fast the envelope can fall "
1282 "(the lower the value the steeper it can fall)",
1283 0.01,
1284 0.2);
1286 "threshold",
1287 0.0,
1288 0.0,
1289 1.0,
1290 "Threshold",
1291 "Minimum amplitude value needed to influence the envelope",
1292 0.01,
1293 0.1);
1295 "use_accumulate",
1296 false,
1297 "Accumulate",
1298 "Only the positive differences of the envelope amplitudes are summarized to "
1299 "produce the output");
1301 "use_additive",
1302 false,
1303 "Additive",
1304 "The amplitudes of the envelope are summarized (or, when Accumulate is enabled, "
1305 "both positive and negative differences are accumulated)");
1307 "use_square",
1308 false,
1309 "Square",
1310 "The output is a square curve (negative values always result in -1, and "
1311 "positive ones in 1)");
1313 "sthreshold",
1314 0.1,
1315 0.0,
1316 1.0,
1317 "Square Threshold",
1318 "Square only: all values with an absolute amplitude lower than that result in 0",
1319 0.01,
1320 0.1);
1321}
1322
1325/* -------------------------------------------------------------------- */
1332/* Evaluates the curves between each selected keyframe on each frame, and keys the value. */
1334{
1335 ListBase anim_data = {nullptr, nullptr};
1336 int filter;
1337
1338 /* filter data */
1342 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
1343
1344 /* Loop through filtered data and add keys between selected keyframes on every frame. */
1345 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
1347
1348 ale->update |= ANIM_UPDATE_DEPS;
1349 }
1350
1351 ANIM_animdata_update(ac, &anim_data);
1352 ANIM_animdata_freelist(&anim_data);
1353}
1354
1355/* ------------------- */
1356
1358{
1359 bAnimContext ac;
1360
1361 /* Get editor data. */
1362 if (ANIM_animdata_get_context(C, &ac) == 0) {
1363 return OPERATOR_CANCELLED;
1364 }
1365
1366 /* Bake keyframes. */
1367 bake_graph_keys(&ac);
1368
1369 /* Set notifier that keyframes have changed. */
1371
1372 return OPERATOR_FINISHED;
1373}
1374
1376{
1377 /* Identifiers */
1378 ot->name = "Bake Keyframes";
1379 ot->idname = "GRAPH_OT_bake_keys";
1380 ot->description = "Add keyframes on every frame between the selected keyframes";
1381
1382 /* API callbacks */
1385
1386 /* Flags */
1388}
1389
1392/* ************************************************************************** */
1393/* EXTRAPOLATION MODE AND KEYFRAME HANDLE SETTINGS */
1394
1395/* -------------------------------------------------------------------- */
1399/* Defines for make/clear cyclic extrapolation tools. */
1400#define MAKE_CYCLIC_EXPO -1
1401#define CLEAR_CYCLIC_EXPO -2
1402
1403/* Defines for set extrapolation-type for selected keyframes tool. */
1406 "CONSTANT",
1407 0,
1408 "Constant Extrapolation",
1409 "Values on endpoint keyframes are held"},
1411 "LINEAR",
1412 0,
1413 "Linear Extrapolation",
1414 "Straight-line slope of end segments are extended past the endpoint keyframes"},
1415
1417 "MAKE_CYCLIC",
1418 0,
1419 "Make Cyclic (F-Modifier)",
1420 "Add Cycles F-Modifier if one doesn't exist already"},
1422 "CLEAR_CYCLIC",
1423 0,
1424 "Clear Cyclic (F-Modifier)",
1425 "Remove Cycles F-Modifier if not needed anymore"},
1426 {0, nullptr, 0, nullptr, nullptr},
1427};
1428
1429/* This function is responsible for setting extrapolation mode for keyframes. */
1430static void setexpo_graph_keys(bAnimContext *ac, short mode)
1431{
1432 ListBase anim_data = {nullptr, nullptr};
1433 int filter;
1434
1435 /* Filter data. */
1439 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
1440
1441 /* Loop through setting mode per F-Curve. */
1442 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
1443 FCurve *fcu = (FCurve *)ale->data;
1444
1445 if (mode >= 0) {
1446 /* Just set mode setting. */
1447 fcu->extend = mode;
1448
1449 ale->update |= ANIM_UPDATE_HANDLES;
1450 }
1451 else {
1452 /* Shortcuts for managing Cycles F-Modifiers to make it easier to toggle cyclic animation
1453 * without having to go through FModifier UI in Graph Editor to do so.
1454 */
1455 if (mode == MAKE_CYCLIC_EXPO) {
1456 /* Only add if one doesn't exist. */
1458 /* TODO: add some more preset versions which set different extrapolation options?
1459 * (Joshua Leung 2011) */
1461 }
1462 }
1463 else if (mode == CLEAR_CYCLIC_EXPO) {
1464 /* Remove all the modifiers fitting this description. */
1465 FModifier *fcm, *fcn = nullptr;
1466
1467 for (fcm = static_cast<FModifier *>(fcu->modifiers.first); fcm; fcm = fcn) {
1468 fcn = fcm->next;
1469
1470 if (fcm->type == FMODIFIER_TYPE_CYCLES) {
1471 remove_fmodifier(&fcu->modifiers, fcm);
1472 }
1473 }
1474 }
1475 }
1476
1477 ale->update |= ANIM_UPDATE_DEPS;
1478 }
1479
1480 ANIM_animdata_update(ac, &anim_data);
1481 ANIM_animdata_freelist(&anim_data);
1482}
1483
1484/* ------------------- */
1485
1487{
1488 bAnimContext ac;
1489 short mode;
1490
1491 /* Get editor data. */
1492 if (ANIM_animdata_get_context(C, &ac) == 0) {
1493 return OPERATOR_CANCELLED;
1494 }
1495
1496 /* Get handle setting mode. */
1497 mode = RNA_enum_get(op->ptr, "type");
1498
1499 /* Set handle type. */
1500 setexpo_graph_keys(&ac, mode);
1501
1502 /* Set notifier that keyframe properties have changed. */
1504
1505 return OPERATOR_FINISHED;
1506}
1507
1509{
1510 /* Identifiers */
1511 ot->name = "Set F-Curve Extrapolation";
1512 ot->idname = "GRAPH_OT_extrapolation_type";
1513 ot->description = "Set extrapolation mode for selected F-Curves";
1514
1515 /* API callbacks */
1519
1520 /* Flags */
1522
1523 /* Id-props */
1524 ot->prop = RNA_def_enum(ot->srna, "type", prop_graphkeys_expo_types, 0, "Type", "");
1525}
1526
1529/* -------------------------------------------------------------------- */
1533/* This function is responsible for setting interpolation mode for keyframes. */
1534static void setipo_graph_keys(bAnimContext *ac, short mode)
1535{
1536 ListBase anim_data = {nullptr, nullptr};
1537 int filter;
1539
1540 /* Filter data. */
1544 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
1545
1546 /* Loop through setting BezTriple interpolation
1547 * NOTE: we do not supply KeyframeEditData to the looper yet.
1548 * Currently that's not necessary here.
1549 */
1550 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
1552 nullptr, static_cast<FCurve *>(ale->key_data), nullptr, set_cb, BKE_fcurve_handles_recalc);
1553
1554 ale->update |= ANIM_UPDATE_DEFAULT_NOHANDLES;
1555 }
1556
1557 ANIM_animdata_update(ac, &anim_data);
1558 ANIM_animdata_freelist(&anim_data);
1559}
1560
1561/* ------------------- */
1562
1564{
1565 bAnimContext ac;
1566 short mode;
1567
1568 /* Get editor data. */
1569 if (ANIM_animdata_get_context(C, &ac) == 0) {
1570 return OPERATOR_CANCELLED;
1571 }
1572
1573 /* Get handle setting mode. */
1574 mode = RNA_enum_get(op->ptr, "type");
1575
1576 /* Set handle type. */
1577 setipo_graph_keys(&ac, mode);
1578
1579 /* Set notifier that keyframe properties have changed. */
1581
1582 return OPERATOR_FINISHED;
1583}
1584
1586{
1587 /* Identifiers */
1588 ot->name = "Set Keyframe Interpolation";
1589 ot->idname = "GRAPH_OT_interpolation_type";
1590 ot->description =
1591 "Set interpolation mode for the F-Curve segments starting from the selected keyframes";
1592
1593 /* API callbacks */
1597
1598 /* Flags */
1600
1601 /* Id-props */
1602 ot->prop = RNA_def_enum(
1603 ot->srna, "type", rna_enum_beztriple_interpolation_mode_items, 0, "Type", "");
1605}
1606
1609/* -------------------------------------------------------------------- */
1613static void seteasing_graph_keys(bAnimContext *ac, short mode)
1614{
1615 ListBase anim_data = {nullptr, nullptr};
1616 int filter;
1618
1619 /* Filter data. */
1623 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
1624
1625 /* Loop through setting BezTriple easing.
1626 * NOTE: we do not supply KeyframeEditData to the looper yet.
1627 * Currently that's not necessary here.
1628 */
1629 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
1631 nullptr, static_cast<FCurve *>(ale->key_data), nullptr, set_cb, BKE_fcurve_handles_recalc);
1632
1633 ale->update |= ANIM_UPDATE_DEFAULT_NOHANDLES;
1634 }
1635
1636 ANIM_animdata_update(ac, &anim_data);
1637 ANIM_animdata_freelist(&anim_data);
1638}
1639
1641{
1642 bAnimContext ac;
1643 short mode;
1644
1645 /* Get editor data. */
1646 if (ANIM_animdata_get_context(C, &ac) == 0) {
1647 return OPERATOR_CANCELLED;
1648 }
1649
1650 /* Get handle setting mode. */
1651 mode = RNA_enum_get(op->ptr, "type");
1652
1653 /* Set handle type. */
1654 seteasing_graph_keys(&ac, mode);
1655
1656 /* Set notifier that keyframe properties have changed. */
1658
1659 return OPERATOR_FINISHED;
1660}
1661
1663{
1664 /* Identifiers */
1665 ot->name = "Set Keyframe Easing Type";
1666 ot->idname = "GRAPH_OT_easing_type";
1667 ot->description =
1668 "Set easing type for the F-Curve segments starting from the selected keyframes";
1669
1670 /* API callbacks */
1674
1675 /* Flags */
1677
1678 /* Id-props */
1679 ot->prop = RNA_def_enum(
1680 ot->srna, "type", rna_enum_beztriple_interpolation_easing_items, 0, "Type", "");
1681}
1682
1685/* -------------------------------------------------------------------- */
1689/* This function is responsible for setting handle-type of selected keyframes. */
1690static void sethandles_graph_keys(bAnimContext *ac, short mode)
1691{
1692 ListBase anim_data = {nullptr, nullptr};
1693 int filter;
1694
1697
1698 /* Filter data. */
1702 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
1703
1704 /* Loop through setting flags for handles.
1705 * NOTE: we do not supply KeyframeEditData to the looper yet.
1706 * Currently that's not necessary here.
1707 */
1708 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
1709 FCurve *fcu = (FCurve *)ale->key_data;
1710
1711 /* Any selected keyframes for editing? */
1712 if (ANIM_fcurve_keyframes_loop(nullptr, fcu, nullptr, sel_cb, nullptr)) {
1713 /* Change type of selected handles. */
1714 ANIM_fcurve_keyframes_loop(nullptr, fcu, nullptr, edit_cb, BKE_fcurve_handles_recalc);
1715
1716 ale->update |= ANIM_UPDATE_DEFAULT;
1717 }
1718 }
1719
1720 ANIM_animdata_update(ac, &anim_data);
1721 ANIM_animdata_freelist(&anim_data);
1722}
1723/* ------------------- */
1724
1726{
1727 bAnimContext ac;
1728 short mode;
1729
1730 /* Get editor data. */
1731 if (ANIM_animdata_get_context(C, &ac) == 0) {
1732 return OPERATOR_CANCELLED;
1733 }
1734
1735 /* Get handle setting mode. */
1736 mode = RNA_enum_get(op->ptr, "type");
1737
1738 /* Set handle type. */
1739 sethandles_graph_keys(&ac, mode);
1740
1741 /* Set notifier that keyframe properties have changed. */
1743
1744 return OPERATOR_FINISHED;
1745}
1746
1748{
1749 /* Identifiers */
1750 ot->name = "Set Keyframe Handle Type";
1751 ot->idname = "GRAPH_OT_handle_type";
1752 ot->description = "Set type of handle for selected keyframes";
1753
1754 /* API callbacks */
1758
1759 /* Flags */
1761
1762 /* Id-props */
1763 ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_keyframe_handle_type_items, 0, "Type", "");
1764}
1765
1768/* ************************************************************************** */
1769/* EULER FILTER */
1770
1771/* -------------------------------------------------------------------- */
1780/* Set of three euler-rotation F-Curves. */
1791
1792static bool keyframe_time_differs(BezTriple *keyframes[3])
1793{
1794 const float precision = 1e-5;
1795 return fabs(keyframes[0]->vec[1][0] - keyframes[1]->vec[1][0]) > precision ||
1796 fabs(keyframes[1]->vec[1][0] - keyframes[2]->vec[1][0]) > precision ||
1797 fabs(keyframes[0]->vec[1][0] - keyframes[2]->vec[1][0]) > precision;
1798}
1799
1800/* Find groups of `rotation_euler` channels. */
1802 const ListBase /*bAnimListElem*/ *anim_data, ReportList *reports, int *r_num_groups)
1803{
1804 ListBase euler_groups = {nullptr, nullptr};
1805 tEulerFilter *euf = nullptr;
1806 *r_num_groups = 0;
1807
1808 LISTBASE_FOREACH (bAnimListElem *, ale, anim_data) {
1809 FCurve *const fcu = (FCurve *)ale->data;
1810
1811 /* Check if this is an appropriate F-Curve:
1812 * - Only rotation curves.
1813 * - For pchan curves, make sure we're only using the euler curves.
1814 */
1815 if (strstr(fcu->rna_path, "rotation_euler") == nullptr) {
1816 continue;
1817 }
1818 if (ELEM(fcu->array_index, 0, 1, 2) == 0) {
1819 BKE_reportf(reports,
1821 "Euler Rotation F-Curve has invalid index (ID='%s', Path='%s', Index=%d)",
1822 (ale->id) ? ale->id->name : RPT_("<No ID>"),
1823 fcu->rna_path,
1824 fcu->array_index);
1825 continue;
1826 }
1827
1828 /* Assume that this animation channel will be touched by the Euler filter. Doing this here
1829 * saves another loop over the animation data. */
1830 ale->update |= ANIM_UPDATE_DEFAULT;
1831
1832 /* Optimization: assume that XYZ curves will always be stored consecutively,
1833 * so if the paths or the ID's don't match up, then a curve needs to be added
1834 * to a new group.
1835 */
1836 if ((euf) && (euf->id == ale->id) && STREQ(euf->rna_path, fcu->rna_path)) {
1837 /* This should be fine to add to the existing group then. */
1838 euf->fcurves[fcu->array_index] = fcu;
1839 continue;
1840 }
1841
1842 /* Just add to a new block. */
1843 euf = static_cast<tEulerFilter *>(MEM_callocN(sizeof(tEulerFilter), "tEulerFilter"));
1844 BLI_addtail(&euler_groups, euf);
1845 ++*r_num_groups;
1846
1847 euf->id = ale->id;
1848 /* This should be safe, since we're only using it for a short time. */
1849 euf->rna_path = fcu->rna_path;
1850 euf->fcurves[fcu->array_index] = fcu;
1851 }
1852
1853 return euler_groups;
1854}
1855
1856/* Perform discontinuity filter based on conversion to matrix and back.
1857 * Return true if the curves were filtered (which may have been a no-op), false otherwise. */
1859{
1860 /* Sanity check: ensure that there are enough F-Curves to work on in this group. */
1861 if (ELEM(nullptr, euf->fcurves[0], euf->fcurves[1], euf->fcurves[2])) {
1862 /* Report which components are missing. */
1863 BKE_reportf(reports,
1864 RPT_INFO,
1865 "Missing %s%s%s component(s) of euler rotation for ID='%s' and RNA-Path='%s'",
1866 (euf->fcurves[0] == nullptr) ? "X" : "",
1867 (euf->fcurves[1] == nullptr) ? "Y" : "",
1868 (euf->fcurves[2] == nullptr) ? "Z" : "",
1869 euf->id->name,
1870 euf->rna_path);
1871 return false;
1872 }
1873
1874 FCurve *fcu_rot_x = euf->fcurves[0];
1875 FCurve *fcu_rot_y = euf->fcurves[1];
1876 FCurve *fcu_rot_z = euf->fcurves[2];
1877 if (fcu_rot_x->totvert != fcu_rot_y->totvert || fcu_rot_y->totvert != fcu_rot_z->totvert) {
1878 BKE_reportf(reports,
1879 RPT_INFO,
1880 "XYZ rotations not equally keyed for ID='%s' and RNA-Path='%s'",
1881 euf->id->name,
1882 euf->rna_path);
1883 return false;
1884 }
1885
1886 if (fcu_rot_x->totvert < 2) {
1887 /* Empty curves and single keyframes are trivially "filtered". */
1888 return false;
1889 }
1890
1891 float filtered_euler[3] = {
1892 fcu_rot_x->bezt[0].vec[1][1],
1893 fcu_rot_y->bezt[0].vec[1][1],
1894 fcu_rot_z->bezt[0].vec[1][1],
1895 };
1896
1897 for (int keyframe_index = 1; keyframe_index < fcu_rot_x->totvert; ++keyframe_index) {
1898 BezTriple *keyframes[3] = {
1899 &fcu_rot_x->bezt[keyframe_index],
1900 &fcu_rot_y->bezt[keyframe_index],
1901 &fcu_rot_z->bezt[keyframe_index],
1902 };
1903
1904 if (keyframe_time_differs(keyframes)) {
1905 /* The X-coordinates of the keyframes are different, so we cannot correct this key. */
1906 continue;
1907 }
1908
1909 const float unfiltered_euler[3] = {
1910 keyframes[0]->vec[1][1],
1911 keyframes[1]->vec[1][1],
1912 keyframes[2]->vec[1][1],
1913 };
1914
1915 /* The conversion back from matrix to Euler angles actually performs the filtering. */
1916 float matrix[3][3];
1917 eul_to_mat3(matrix, unfiltered_euler);
1918 mat3_normalized_to_compatible_eul(filtered_euler, filtered_euler, matrix);
1919
1920 /* TODO(Sybren): it might be a nice touch to compare `filtered_euler` with `unfiltered_euler`,
1921 * to see if there was actually a change. This could improve reporting for the artist. */
1922
1923 BKE_fcurve_keyframe_move_value_with_handles(keyframes[0], filtered_euler[0]);
1924 BKE_fcurve_keyframe_move_value_with_handles(keyframes[1], filtered_euler[1]);
1925 BKE_fcurve_keyframe_move_value_with_handles(keyframes[2], filtered_euler[2]);
1926 }
1927
1928 return true;
1929}
1930
1931/* Remove 360-degree flips from a single FCurve.
1932 * Return true if the curve was modified, false otherwise. */
1934{
1935 /* Simple method: just treat any difference between keys of greater than 180 degrees as being a
1936 * flip. */
1937 BezTriple *bezt, *prev;
1938 uint i;
1939
1940 /* Skip if not enough verts to do a decent analysis. */
1941 if (fcu->totvert <= 2) {
1942 return false;
1943 }
1944
1945 /* Skip baked FCurves. */
1946 if (fcu->bezt == nullptr) {
1947 return false;
1948 }
1949
1950 /* `prev` follows bezt, bezt = "current" point to be fixed. */
1951 /* Our method depends on determining a "difference" from the previous vert. */
1952 bool is_modified = false;
1953 for (i = 1, prev = fcu->bezt, bezt = fcu->bezt + 1; i < fcu->totvert; i++, prev = bezt++) {
1954 const float sign = (prev->vec[1][1] > bezt->vec[1][1]) ? 1.0f : -1.0f;
1955
1956 /* >= 180 degree flip? */
1957 if ((sign * (prev->vec[1][1] - bezt->vec[1][1])) < float(M_PI)) {
1958 continue;
1959 }
1960
1961 /* 360 degrees to add/subtract frame value until difference
1962 * is acceptably small that there's no more flip. */
1963 const float fac = sign * 2.0f * float(M_PI);
1964
1965 while ((sign * (prev->vec[1][1] - bezt->vec[1][1])) >= float(M_PI)) {
1966 bezt->vec[0][1] += fac;
1967 bezt->vec[1][1] += fac;
1968 bezt->vec[2][1] += fac;
1969 }
1970
1971 is_modified = true;
1972 }
1973
1974 return is_modified;
1975}
1976
1977static void euler_filter_perform_filter(ListBase /*tEulerFilter*/ *eulers,
1978 ReportList *reports,
1979 int *r_curves_filtered,
1980 int *r_curves_seen)
1981{
1982 *r_curves_filtered = 0;
1983 *r_curves_seen = 0;
1984
1985 LISTBASE_FOREACH (tEulerFilter *, euf, eulers) {
1986 int curves_filtered_this_group = 0;
1987
1988 if (euler_filter_multi_channel(euf, reports)) {
1989 curves_filtered_this_group = 3;
1990 }
1991
1992 for (int channel_index = 0; channel_index < 3; channel_index++) {
1993 FCurve *fcu = euf->fcurves[channel_index];
1994 if (fcu == nullptr) {
1995 continue;
1996 }
1997 ++*r_curves_seen;
1998
1999 if (euler_filter_single_channel(fcu)) {
2000 ++curves_filtered_this_group;
2001 }
2002 }
2003
2004 *r_curves_filtered += min_ii(3, curves_filtered_this_group);
2005 }
2006}
2007
2009{
2010 /* Get editor data. */
2011 bAnimContext ac;
2012 if (ANIM_animdata_get_context(C, &ac) == 0) {
2013 return OPERATOR_CANCELLED;
2014 }
2015
2016 /* The process is done in two passes:
2017 * 1) Sets of three related rotation curves are identified from the selected channels,
2018 * and are stored as a single 'operation unit' for the next step.
2019 * 2) Each set of three F-Curves is processed for each keyframe, with the values being
2020 * processed as necessary.
2021 */
2022
2023 /* Step 1: extract only the rotation f-curves. */
2026 ListBase anim_data = {nullptr, nullptr};
2028 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
2029
2030 int groups = 0;
2031 ListBase eulers = euler_filter_group_channels(&anim_data, op->reports, &groups);
2032 BLI_assert(BLI_listbase_count(&eulers) == groups);
2033
2034 if (groups == 0) {
2035 ANIM_animdata_freelist(&anim_data);
2036 BKE_report(op->reports, RPT_WARNING, "No Euler Rotation F-Curves to fix up");
2037 return OPERATOR_CANCELLED;
2038 }
2039
2040 /* Step 2: go through each set of curves, processing the values at each keyframe.
2041 * - It is assumed that there must be a full set of keyframes at each keyframe position.
2042 */
2043 int curves_filtered;
2044 int curves_seen;
2045 euler_filter_perform_filter(&eulers, op->reports, &curves_filtered, &curves_seen);
2046
2047 BLI_freelistN(&eulers);
2048 ANIM_animdata_update(&ac, &anim_data);
2049 ANIM_animdata_freelist(&anim_data);
2050
2051 if (curves_filtered == 0) {
2052 if (curves_seen < 3) {
2053 /* Showing the entire error message makes no sense when the artist is only trying to filter
2054 * one or two curves. */
2055 BKE_report(op->reports, RPT_WARNING, "No Euler Rotations could be corrected");
2056 }
2057 else {
2058 BKE_report(op->reports,
2059 RPT_ERROR,
2060 "No Euler Rotations could be corrected, ensure each rotation has keys for all "
2061 "components, "
2062 "and that F-Curves for these are in consecutive XYZ order and selected");
2063 }
2064 return OPERATOR_CANCELLED;
2065 }
2066
2067 if (curves_filtered != curves_seen) {
2068 BLI_assert(curves_filtered < curves_seen);
2069 BKE_reportf(op->reports,
2070 RPT_INFO,
2071 "%d of %d rotation channels were filtered (see the Info window for details)",
2072 curves_filtered,
2073 curves_seen);
2074 }
2075 else if (curves_seen == 1) {
2076 BKE_report(op->reports, RPT_INFO, "The rotation channel was filtered");
2077 }
2078 else {
2079 BKE_reportf(op->reports, RPT_INFO, "All %d rotation channels were filtered", curves_seen);
2080 }
2081
2082 /* Set notifier that keyframes have changed. */
2084
2085 /* Done at last. */
2086 return OPERATOR_FINISHED;
2087}
2088
2090{
2091 /* Identifiers */
2092 ot->name = "Euler Discontinuity Filter";
2093 ot->idname = "GRAPH_OT_euler_filter";
2094 ot->description =
2095 "Fix large jumps and flips in the selected "
2096 "Euler Rotation F-Curves arising from rotation "
2097 "values being clipped when baking physics";
2098
2099 /* API callbacks */
2102
2103 /* Flags */
2105}
2106
2109/* ************************************************************************** */
2110/* SNAPPING */
2111
2112/* -------------------------------------------------------------------- */
2117{
2118 /* Prevent changes during render. */
2119 if (G.is_rendering) {
2120 return false;
2121 }
2122
2124}
2125
2127{
2128 ListBase anim_data = {nullptr, nullptr};
2129 int filter;
2130 KeyframeEditData ked;
2131
2132 /* Init edit data. */
2133 memset(&ked, 0, sizeof(KeyframeEditData));
2134
2135 /* Loop over action data, averaging values. */
2139 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
2140
2141 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
2142 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
2143 short mapping_flag = ANIM_get_normalization_flags(ac->sl);
2144 KeyframeEditData current_ked;
2145 float offset;
2146 float unit_scale = ANIM_unit_mapping_get_factor(ac->scene,
2147 ale->id,
2148 static_cast<FCurve *>(ale->key_data),
2149 mapping_flag | ANIM_UNITCONV_ONLYKEYS,
2150 &offset);
2151
2152 memset(&current_ked, 0, sizeof(current_ked));
2153
2154 if (adt) {
2155 ANIM_nla_mapping_apply_fcurve(adt, static_cast<FCurve *>(ale->key_data), false, true);
2157 &current_ked, static_cast<FCurve *>(ale->key_data), nullptr, bezt_calc_average, nullptr);
2158 ANIM_nla_mapping_apply_fcurve(adt, static_cast<FCurve *>(ale->key_data), true, true);
2159 }
2160 else {
2162 &current_ked, static_cast<FCurve *>(ale->key_data), nullptr, bezt_calc_average, nullptr);
2163 }
2164
2165 if (current_ked.i1 == 0) {
2166 continue;
2167 }
2168
2169 ked.f1 += current_ked.f1;
2170 ked.i1 += current_ked.i1;
2171 ked.f2 += (current_ked.f2 + offset * current_ked.i1) * unit_scale;
2172 ked.i2 += current_ked.i2;
2173 }
2174
2175 ANIM_animdata_freelist(&anim_data);
2176
2177 return ked;
2178}
2179
2180/* Snap current-frame indicator to 'average time' of selected keyframe. */
2182{
2183 bAnimContext ac;
2184
2185 /* Get editor data. */
2186 if (ANIM_animdata_get_context(C, &ac) == 0) {
2187 return OPERATOR_CANCELLED;
2188 }
2189
2190 const KeyframeEditData keyframe_sum = sum_selected_keyframes(&ac);
2191 const float sum_time = keyframe_sum.f1;
2192 const float sum_value = keyframe_sum.f2;
2193 const int num_keyframes = keyframe_sum.i1;
2194
2195 if (num_keyframes == 0) {
2196 return OPERATOR_FINISHED;
2197 }
2198
2199 /* Set the new current frame and cursor values, based on the average time and value. */
2200 SpaceGraph *sipo = (SpaceGraph *)ac.sl;
2201 Scene *scene = ac.scene;
2202
2203 /* Take the average values, rounding to the nearest int as necessary for int results. */
2204 if (sipo->mode == SIPO_MODE_DRIVERS) {
2205 /* Drivers Mode - Affects cursor (float) */
2206 sipo->cursorTime = sum_time / float(num_keyframes);
2207 }
2208 else {
2209 /* Animation Mode - Affects current frame (int) */
2210 scene->r.cfra = round_fl_to_int(sum_time / num_keyframes);
2211 scene->r.subframe = 0.0f;
2212 }
2213 sipo->cursorVal = sum_value / float(num_keyframes);
2214
2215 /* Set notifier that things have changed. */
2217
2218 return OPERATOR_FINISHED;
2219}
2220
2222{
2223 /* Identifiers */
2224 ot->name = "Jump to Keyframes";
2225 ot->idname = "GRAPH_OT_frame_jump";
2226 ot->description = "Place the cursor on the midpoint of selected keyframes";
2227
2228 /* API callbacks */
2231
2232 /* Flags */
2234}
2235
2236static bool find_closest_frame(const FCurve *fcu,
2237 const float frame,
2238 const bool next,
2239 float *r_closest_frame)
2240{
2241 bool replace;
2242 int bezt_index = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, frame, fcu->totvert, &replace);
2243
2244 BezTriple *bezt;
2245 if (next) {
2246 if (replace) {
2247 bezt_index++;
2248 }
2249 if (bezt_index > fcu->totvert - 1) {
2250 return false;
2251 }
2252 bezt = &fcu->bezt[bezt_index];
2253 }
2254 else {
2255 if (bezt_index - 1 < 0) {
2256 return false;
2257 }
2258 bezt = &fcu->bezt[bezt_index - 1];
2259 }
2260
2261 *r_closest_frame = bezt->vec[1][0];
2262 return true;
2263}
2264
2266{
2267 bAnimContext ac;
2268 Scene *scene = CTX_data_scene(C);
2269
2270 bool next = RNA_boolean_get(op->ptr, "next");
2271
2272 /* Get editor data. */
2273 if (ANIM_animdata_get_context(C, &ac) == 0) {
2274 return OPERATOR_CANCELLED;
2275 }
2276
2277 ListBase anim_data = {nullptr, nullptr};
2280 if (U.animation_flag & USER_ANIM_ONLY_SHOW_SELECTED_CURVE_KEYS) {
2281 filter |= ANIMFILTER_SEL;
2282 }
2283
2285 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
2286
2287 float closest_frame = next ? FLT_MAX : -FLT_MAX;
2288 bool found = false;
2289
2290 const float current_frame = BKE_scene_frame_get(scene);
2291 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
2292 FCurve *fcu = static_cast<FCurve *>(ale->key_data);
2293 if (!fcu->bezt) {
2294 continue;
2295 }
2296 AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
2297
2298 float closest_fcu_frame;
2299 ANIM_nla_mapping_apply_fcurve(adt, fcu, false, true);
2300 const bool success = find_closest_frame(fcu, current_frame, next, &closest_fcu_frame);
2301 ANIM_nla_mapping_apply_fcurve(adt, fcu, true, true);
2302
2303 if (!success) {
2304 continue;
2305 }
2306
2307 if ((next && closest_fcu_frame < closest_frame) ||
2308 (!next && closest_fcu_frame > closest_frame))
2309 {
2310 closest_frame = closest_fcu_frame;
2311 found = true;
2312 }
2313 }
2314
2315 ANIM_animdata_freelist(&anim_data);
2316
2317 if (!found) {
2318 BKE_report(op->reports, RPT_INFO, "No more keyframes to jump to in this direction");
2319 return OPERATOR_CANCELLED;
2320 }
2321
2322 BKE_scene_frame_set(scene, closest_frame);
2323
2324 /* Set notifier that things have changed. */
2326 return OPERATOR_FINISHED;
2327}
2328
2330{
2331 ot->name = "Jump to Keyframe";
2332 ot->description = "Jump to previous/next keyframe";
2333 ot->idname = "GRAPH_OT_keyframe_jump";
2334
2336
2339 ot->undo_group = "Frame Change";
2340
2341 /* properties */
2342 RNA_def_boolean(ot->srna, "next", true, "Next Keyframe", "");
2343}
2344
2345/* snap 2D cursor value to the average value of selected keyframe */
2347{
2348 bAnimContext ac;
2349
2350 if (ANIM_animdata_get_context(C, &ac) == 0) {
2351 return OPERATOR_CANCELLED;
2352 }
2353
2354 const KeyframeEditData keyframe_sum = sum_selected_keyframes(&ac);
2355 const float sum_value = keyframe_sum.f2;
2356 const int num_keyframes = keyframe_sum.i1;
2357
2358 if (num_keyframes == 0) {
2359 return OPERATOR_FINISHED;
2360 }
2361
2362 SpaceGraph *sipo = (SpaceGraph *)ac.sl;
2363 sipo->cursorVal = sum_value / float(num_keyframes);
2364 // WM_event_add_notifier(C, NC_SCENE | ND_FRAME, ac.scene);
2366
2367 return OPERATOR_FINISHED;
2368}
2369
2371{
2372 /* Identifiers. */
2373 ot->name = "Snap Cursor Value to Selected";
2374 ot->idname = "GRAPH_OT_snap_cursor_value";
2375 ot->description = "Place the cursor value on the average value of selected keyframes";
2376
2377 /* API callbacks. */
2380
2381 /* Flags */
2383}
2384
2387/* -------------------------------------------------------------------- */
2391/* Defines for snap keyframes tool. */
2394 "CFRA",
2395 0,
2396 "Selection to Current Frame",
2397 "Snap selected keyframes to the current frame"},
2399 "VALUE",
2400 0,
2401 "Selection to Cursor Value",
2402 "Set values of selected keyframes to the cursor value (Y/Horizontal component)"},
2404 "NEAREST_FRAME",
2405 0,
2406 "Selection to Nearest Frame",
2407 "Snap selected keyframes to the nearest (whole) frame (use to fix accidental subframe "
2408 "offsets)"},
2410 "NEAREST_SECOND",
2411 0,
2412 "Selection to Nearest Second",
2413 "Snap selected keyframes to the nearest second"},
2415 "NEAREST_MARKER",
2416 0,
2417 "Selection to Nearest Marker",
2418 "Snap selected keyframes to the nearest marker"},
2420 "HORIZONTAL",
2421 0,
2422 "Flatten Handles",
2423 "Flatten handles for a smoother transition"},
2424 {0, nullptr, 0, nullptr, nullptr},
2425};
2426
2427/* This function is responsible for snapping keyframes to frame-times. */
2428static void snap_graph_keys(bAnimContext *ac, short mode)
2429{
2430 ListBase anim_data = {nullptr, nullptr};
2431 int filter;
2432
2433 SpaceGraph *sipo = (SpaceGraph *)ac->sl;
2434 KeyframeEditData ked;
2435 KeyframeEditFunc edit_cb;
2436 float cursor_value = 0.0f;
2437
2438 /* Filter data. */
2442 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
2443
2444 /* Init custom data for iterating over keyframes. */
2445 memset(&ked, 0, sizeof(KeyframeEditData));
2446 ked.scene = ac->scene;
2447 if (mode == GRAPHKEYS_SNAP_NEAREST_MARKER) {
2448 ked.list.first = (ac->markers) ? ac->markers->first : nullptr;
2449 ked.list.last = (ac->markers) ? ac->markers->last : nullptr;
2450 }
2451 else if (mode == GRAPHKEYS_SNAP_VALUE) {
2452 cursor_value = (sipo) ? sipo->cursorVal : 0.0f;
2453 }
2454 else if (mode == GRAPHKEYS_SNAP_CFRA) {
2455 /* In drivers mode, use the cursor value instead
2456 * (We need to use a different callback for that though)
2457 */
2458 if (sipo->mode == SIPO_MODE_DRIVERS) {
2459 ked.f1 = sipo->cursorTime;
2460 mode = SNAP_KEYS_TIME;
2461 }
2462 }
2463
2464 /* Get beztriple editing callbacks. */
2465 edit_cb = ANIM_editkeyframes_snap(mode);
2466
2467 /* Snap keyframes. */
2468 const bool use_handle = (sipo->flag & SIPO_NOHANDLES) == 0;
2469 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
2470 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
2471
2472 /* Normalize cursor value (for normalized F-Curves display). */
2473 if (mode == GRAPHKEYS_SNAP_VALUE) {
2474 short mapping_flag = ANIM_get_normalization_flags(ac->sl);
2475 float offset;
2476 float unit_scale = ANIM_unit_mapping_get_factor(
2477 ac->scene, ale->id, static_cast<FCurve *>(ale->key_data), mapping_flag, &offset);
2478
2479 ked.f1 = (cursor_value / unit_scale) - offset;
2480 }
2481
2482 /* Perform snapping. */
2483 if (adt) {
2484 ANIM_nla_mapping_apply_fcurve(adt, static_cast<FCurve *>(ale->key_data), false, false);
2486 &ked, static_cast<FCurve *>(ale->key_data), nullptr, edit_cb, BKE_fcurve_handles_recalc);
2488 static_cast<FCurve *>(ale->key_data), BEZT_FLAG_TEMP_TAG, use_handle);
2489 ANIM_nla_mapping_apply_fcurve(adt, static_cast<FCurve *>(ale->key_data), true, false);
2490 }
2491 else {
2493 &ked, static_cast<FCurve *>(ale->key_data), nullptr, edit_cb, BKE_fcurve_handles_recalc);
2495 static_cast<FCurve *>(ale->key_data), BEZT_FLAG_TEMP_TAG, use_handle);
2496 }
2497
2498 ale->update |= ANIM_UPDATE_DEFAULT;
2499 }
2500
2501 ANIM_animdata_update(ac, &anim_data);
2502 ANIM_animdata_freelist(&anim_data);
2503}
2504
2505/* ------------------- */
2506
2508{
2509 bAnimContext ac;
2510 short mode;
2511
2512 /* Get editor data. */
2513 if (ANIM_animdata_get_context(C, &ac) == 0) {
2514 return OPERATOR_CANCELLED;
2515 }
2516
2517 /* Get snapping mode. */
2518 mode = RNA_enum_get(op->ptr, "type");
2519
2520 /* Snap keyframes. */
2521 snap_graph_keys(&ac, mode);
2522
2523 /* Set notifier that keyframes have changed. */
2525
2526 return OPERATOR_FINISHED;
2527}
2528
2530{
2531 bAnimContext ac;
2532 ListBase anim_data = {nullptr, nullptr};
2533
2534 /* Get editor data. */
2535 if (ANIM_animdata_get_context(C, &ac) == 0) {
2536 return false;
2537 }
2538
2539 /* Filter data. */
2543 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
2544
2545 /* Check if any of the visible and editable f-curves have at least one selected control point. */
2546 bool has_selected_control_points = false;
2547 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
2548 const FCurve *fcu = static_cast<const FCurve *>(ale->key_data);
2550 has_selected_control_points = true;
2551 break;
2552 }
2553 }
2554
2555 ANIM_animdata_freelist(&anim_data);
2556
2557 return has_selected_control_points;
2558}
2559
2561 wmOperator *op,
2562 const wmEvent *event)
2563{
2565 BKE_report(op->reports, RPT_ERROR, "No control points are selected");
2566 return OPERATOR_CANCELLED;
2567 }
2568
2569 return WM_menu_invoke(C, op, event);
2570}
2571
2573{
2574 /* Identifiers */
2575 ot->name = "Snap Keys";
2576 ot->idname = "GRAPH_OT_snap";
2577 ot->description = "Snap selected keyframes to the chosen times/values";
2578
2579 /* API callbacks */
2583
2584 /* Flags */
2586
2587 /* Id-props */
2588 ot->prop = RNA_def_enum(ot->srna, "type", prop_graphkeys_snap_types, 0, "Type", "");
2589}
2590
2593/* -------------------------------------------------------------------- */
2597/* Defines for equalize handles tool. */
2599 {GRAPHKEYS_EQUALIZE_LEFT, "LEFT", 0, "Left", "Equalize selected keyframes' left handles"},
2600 {GRAPHKEYS_EQUALIZE_RIGHT, "RIGHT", 0, "Right", "Equalize selected keyframes' right handles"},
2601 {GRAPHKEYS_EQUALIZE_BOTH, "BOTH", 0, "Both", "Equalize both of a keyframe's handles"},
2602 {0, nullptr, 0, nullptr, nullptr},
2603};
2604
2605/* ------------------- */
2606
2607/* Equalize selected keyframes' bezier handles. */
2608static void equalize_graph_keys(bAnimContext *ac, int mode, float handle_length, bool flatten)
2609{
2610 /* Filter data. */
2613 ListBase anim_data = {nullptr, nullptr};
2615 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
2616
2617 /* Equalize keyframes. */
2618 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
2619 ANIM_fcurve_equalize_keyframes_loop(static_cast<FCurve *>(ale->key_data),
2621 handle_length,
2622 flatten);
2623 ale->update |= ANIM_UPDATE_DEFAULT;
2624 }
2625
2626 ANIM_animdata_update(ac, &anim_data);
2627 ANIM_animdata_freelist(&anim_data);
2628}
2629
2631{
2632 bAnimContext ac;
2633
2634 /* Get editor data. */
2635 if (ANIM_animdata_get_context(C, &ac) == 0) {
2636 return OPERATOR_CANCELLED;
2637 }
2638
2639 /* Get equalize mode. */
2640 int mode = RNA_enum_get(op->ptr, "side");
2641 float handle_length = RNA_float_get(op->ptr, "handle_length");
2642 bool flatten = RNA_boolean_get(op->ptr, "flatten");
2643
2644 /* Equalize graph keyframes. */
2645 equalize_graph_keys(&ac, mode, handle_length, flatten);
2646
2648
2649 return OPERATOR_FINISHED;
2650}
2651
2653{
2654 /* Identifiers */
2655 ot->name = "Equalize Handles";
2656 ot->idname = "GRAPH_OT_equalize_handles";
2657 ot->description =
2658 "Ensure selected keyframes' handles have equal length, optionally making them horizontal. "
2659 "Automatic, Automatic Clamped, or Vector handle types will be converted to Aligned";
2660 /* API callbacks */
2664
2665 /* Flags */
2667
2668 /* Properties */
2670 "side",
2672 0,
2673 "Side",
2674 "Side of the keyframes' Bézier handles to affect");
2676 "handle_length",
2677 5.0f,
2678 0.1f,
2679 FLT_MAX,
2680 "Handle Length",
2681 "Length to make selected keyframes' Bézier handles",
2682 1.0f,
2683 50.0f);
2685 ot->srna,
2686 "flatten",
2687 false,
2688 "Flatten",
2689 "Make the values of the selected keyframes' handles the same as their respective keyframes");
2690}
2691
2694/* -------------------------------------------------------------------- */
2698/* Defines for mirror keyframes tool. */
2701 "CFRA",
2702 0,
2703 "By Times Over Current Frame",
2704 "Flip times of selected keyframes using the current frame as the mirror line"},
2706 "VALUE",
2707 0,
2708 "By Values Over Cursor Value",
2709 "Flip values of selected keyframes using the cursor value (Y/Horizontal component) as the "
2710 "mirror line"},
2712 "YAXIS",
2713 0,
2714 "By Times Over Zero Time",
2715 "Flip times of selected keyframes, effectively reversing the order they appear in"},
2717 "XAXIS",
2718 0,
2719 "By Values Over Zero Value",
2720 "Flip values of selected keyframes (i.e. negative values become positive, and vice versa)"},
2722 "MARKER",
2723 0,
2724 "By Times Over First Selected Marker",
2725 "Flip times of selected keyframes using the first selected marker as the reference point"},
2726 {0, nullptr, 0, nullptr, nullptr},
2727};
2728
2729/* This function is responsible for mirroring keyframes. */
2730static void mirror_graph_keys(bAnimContext *ac, short mode)
2731{
2732 ListBase anim_data = {nullptr, nullptr};
2733 int filter;
2734
2735 SpaceGraph *sipo = (SpaceGraph *)ac->sl;
2736 KeyframeEditData ked;
2737 KeyframeEditFunc edit_cb;
2738 float cursor_value = 0.0f;
2739
2740 /* Init custom data for looping over keyframes. */
2741 memset(&ked, 0, sizeof(KeyframeEditData));
2742 ked.scene = ac->scene;
2743
2744 /* Store mode-specific custom data... */
2745 if (mode == GRAPHKEYS_MIRROR_MARKER) {
2746 TimeMarker *marker = nullptr;
2747
2748 /* Find first selected marker. */
2750
2751 /* Store marker's time (if available). */
2752 if (marker) {
2753 ked.f1 = float(marker->frame);
2754 }
2755 else {
2756 return;
2757 }
2758 }
2759 else if (mode == GRAPHKEYS_MIRROR_VALUE) {
2760 cursor_value = (sipo) ? sipo->cursorVal : 0.0f;
2761 }
2762 else if (mode == GRAPHKEYS_MIRROR_CFRA) {
2763 /* In drivers mode, use the cursor value instead
2764 * (We need to use a different callback for that though)
2765 */
2766 if (sipo->mode == SIPO_MODE_DRIVERS) {
2767 ked.f1 = sipo->cursorTime;
2768 mode = MIRROR_KEYS_TIME;
2769 }
2770 }
2771
2772 /* Get beztriple editing callbacks. */
2773 edit_cb = ANIM_editkeyframes_mirror(mode);
2774
2775 /* Filter data. */
2779 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
2780
2781 /* Mirror keyframes. */
2782 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
2783 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
2784
2785 /* Apply unit corrections. */
2786 if (mode == GRAPHKEYS_MIRROR_VALUE) {
2787 short mapping_flag = ANIM_get_normalization_flags(ac->sl);
2788 float offset;
2789 float unit_scale = ANIM_unit_mapping_get_factor(ac->scene,
2790 ale->id,
2791 static_cast<FCurve *>(ale->key_data),
2792 mapping_flag | ANIM_UNITCONV_ONLYKEYS,
2793 &offset);
2794
2795 ked.f1 = (cursor_value - offset) / unit_scale;
2796 }
2797
2798 /* Perform actual mirroring. */
2799 if (adt) {
2800 ANIM_nla_mapping_apply_fcurve(adt, static_cast<FCurve *>(ale->key_data), false, false);
2802 &ked, static_cast<FCurve *>(ale->key_data), nullptr, edit_cb, BKE_fcurve_handles_recalc);
2803 ANIM_nla_mapping_apply_fcurve(adt, static_cast<FCurve *>(ale->key_data), true, false);
2804 }
2805 else {
2807 &ked, static_cast<FCurve *>(ale->key_data), nullptr, edit_cb, BKE_fcurve_handles_recalc);
2808 }
2809
2810 ale->update |= ANIM_UPDATE_DEFAULT;
2811 }
2812
2813 ANIM_animdata_update(ac, &anim_data);
2814 ANIM_animdata_freelist(&anim_data);
2815}
2816
2817/* ------------------- */
2818
2820{
2821 bAnimContext ac;
2822 short mode;
2823
2824 /* Get editor data. */
2825 if (ANIM_animdata_get_context(C, &ac) == 0) {
2826 return OPERATOR_CANCELLED;
2827 }
2828
2829 /* Get mirroring mode. */
2830 mode = RNA_enum_get(op->ptr, "type");
2831
2832 /* Mirror keyframes. */
2833 mirror_graph_keys(&ac, mode);
2834
2835 /* Set notifier that keyframes have changed. */
2837
2838 return OPERATOR_FINISHED;
2839}
2840
2842{
2843 /* Identifiers */
2844 ot->name = "Mirror Keys";
2845 ot->idname = "GRAPH_OT_mirror";
2846 ot->description = "Flip selected keyframes over the selected mirror line";
2847
2848 /* API callbacks */
2852
2853 /* Flags */
2855
2856 /* Id-props */
2857 ot->prop = RNA_def_enum(ot->srna, "type", prop_graphkeys_mirror_types, 0, "Type", "");
2858}
2859
2862/* -------------------------------------------------------------------- */
2867{
2868 bAnimContext ac;
2869 ListBase anim_data = {nullptr, nullptr};
2870 int filter;
2871
2872 /* Get editor data. */
2873 if (ANIM_animdata_get_context(C, &ac) == 0) {
2874 return OPERATOR_CANCELLED;
2875 }
2876
2877 /* Filter data. */
2881 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
2882
2883 /* Smooth keyframes. */
2884 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
2885 /* For now, we can only smooth by flattening handles AND smoothing curve values.
2886 * Perhaps the mode argument could be removed, as that functionality is offered through
2887 * Snap->Flatten Handles anyway.
2888 */
2889 smooth_fcurve(static_cast<FCurve *>(ale->key_data));
2890
2891 ale->update |= ANIM_UPDATE_DEFAULT;
2892 }
2893
2894 ANIM_animdata_update(&ac, &anim_data);
2895 ANIM_animdata_freelist(&anim_data);
2896
2897 /* Set notifier that keyframes have changed. */
2899
2900 return OPERATOR_FINISHED;
2901}
2902
2904{
2905 /* Identifiers */
2906 ot->name = "Smooth Keys";
2907 ot->idname = "GRAPH_OT_smooth";
2908 ot->description = "Apply weighted moving means to make selected F-Curves less bumpy";
2909
2910 /* API callbacks */
2913
2914 /* Flags */
2916}
2917
2920/* ************************************************************************** */
2921/* F-CURVE MODIFIERS */
2922
2923/* -------------------------------------------------------------------- */
2928 PointerRNA * /*ptr*/,
2929 PropertyRNA * /*prop*/,
2930 bool *r_free)
2931{
2932 EnumPropertyItem *item = nullptr;
2933 int totitem = 0;
2934 int i = 0;
2935
2936 if (C == nullptr) {
2938 }
2939
2940 /* Start from 1 to skip the 'Invalid' modifier type. */
2941 for (i = 1; i < FMODIFIER_NUM_TYPES; i++) {
2943 int index;
2944
2945 /* Check if modifier is valid for this context. */
2946 if (fmi == nullptr) {
2947 continue;
2948 }
2949
2951 if (index != -1) { /* Not all types are implemented yet... */
2952 RNA_enum_item_add(&item, &totitem, &rna_enum_fmodifier_type_items[index]);
2953 }
2954 }
2955
2956 RNA_enum_item_end(&item, &totitem);
2957 *r_free = true;
2958
2959 return item;
2960}
2961
2963{
2964 bAnimContext ac;
2965 ListBase anim_data = {nullptr, nullptr};
2966 int filter;
2967 short type;
2968
2969 /* Get editor data. */
2970 if (ANIM_animdata_get_context(C, &ac) == 0) {
2971 return OPERATOR_CANCELLED;
2972 }
2973
2974 /* Get type of modifier to add. */
2975 type = RNA_enum_get(op->ptr, "type");
2976
2977 /* Filter data. */
2980 if (RNA_boolean_get(op->ptr, "only_active")) {
2981 /* FIXME: enforce in this case only a single channel to get handled? */
2982 filter |= ANIMFILTER_ACTIVE;
2983 }
2984 else {
2986 }
2988 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
2989
2990 /* Add f-modifier to each curve. */
2991 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
2992 FCurve *fcu = (FCurve *)ale->data;
2993 FModifier *fcm;
2994
2995 /* Add F-Modifier of specified type to active F-Curve, and make it the active one. */
2996 fcm = add_fmodifier(&fcu->modifiers, type, fcu);
2997 if (fcm) {
2998 set_active_fmodifier(&fcu->modifiers, fcm);
2999 }
3000 else {
3001 BKE_report(op->reports, RPT_ERROR, "Modifier could not be added (see console for details)");
3002 break;
3003 }
3004
3005 ale->update |= ANIM_UPDATE_DEPS;
3006 }
3007
3008 ANIM_animdata_update(&ac, &anim_data);
3009 ANIM_animdata_freelist(&anim_data);
3010
3011 /* Set notifier that things have changed. */
3013
3014 return OPERATOR_FINISHED;
3015}
3016
3018{
3019 PropertyRNA *prop;
3020
3021 /* Identifiers */
3022 ot->name = "Add F-Curve Modifier";
3023 ot->idname = "GRAPH_OT_fmodifier_add";
3024 ot->description = "Add F-Modifier to the active/selected F-Curves";
3025
3026 /* API callbacks */
3030
3031 /* Flags */
3033
3034 /* Id-props */
3035 prop = RNA_def_enum(ot->srna, "type", rna_enum_fmodifier_type_items, 0, "Type", "");
3038 ot->prop = prop;
3039
3041 ot->srna, "only_active", false, "Only Active", "Only add F-Modifier to active F-Curve");
3042}
3043
3046/* -------------------------------------------------------------------- */
3051{
3052 bAnimContext ac;
3053 bAnimListElem *ale;
3054 bool ok = false;
3055
3056 /* Get editor data. */
3057 if (ANIM_animdata_get_context(C, &ac) == 0) {
3058 return OPERATOR_CANCELLED;
3059 }
3060
3061 /* Clear buffer first. */
3063
3064 /* Get the active F-Curve. */
3065 ale = get_active_fcurve_channel(&ac);
3066
3067 /* If this exists, call the copy F-Modifiers API function. */
3068 if (ale && ale->data) {
3069 FCurve *fcu = (FCurve *)ale->data;
3070
3071 /* TODO: When 'active' vs 'all' boolean is added, change last param! (Joshua Leung 2010) */
3072 ok = ANIM_fmodifiers_copy_to_buf(&fcu->modifiers, false);
3073
3074 /* Free temp data now. */
3075 MEM_freeN(ale);
3076 }
3077
3078 /* Successful or not? */
3079 if (ok == 0) {
3080 BKE_report(op->reports, RPT_ERROR, "No F-Modifiers available to be copied");
3081 return OPERATOR_CANCELLED;
3082 }
3083 return OPERATOR_FINISHED;
3084}
3085
3087{
3088 /* Identifiers */
3089 ot->name = "Copy F-Modifiers";
3090 ot->idname = "GRAPH_OT_fmodifier_copy";
3091 ot->description = "Copy the F-Modifier(s) of the active F-Curve";
3092
3093 /* API callbacks */
3096
3097 /* Flags */
3099
3100/* Id-props */
3101#if 0
3103 "all",
3104 1,
3105 "All F-Modifiers",
3106 "Copy all the F-Modifiers, instead of just the active one");
3107#endif
3108}
3109
3112/* -------------------------------------------------------------------- */
3117{
3118 bAnimContext ac;
3119
3120 ListBase anim_data = {nullptr, nullptr};
3121 int filter;
3122
3123 const bool replace = RNA_boolean_get(op->ptr, "replace");
3124 bool ok = false;
3125
3126 /* Get editor data. */
3127 if (ANIM_animdata_get_context(C, &ac) == 0) {
3128 return OPERATOR_CANCELLED;
3129 }
3130
3131 /* Filter data. */
3132 if (RNA_boolean_get(op->ptr, "only_active")) {
3133 /* This should be the default (for buttons) - Just paste to the active FCurve. */
3136 }
3137 else {
3138 /* This is only if the operator gets called from a hotkey or search -
3139 * Paste to all visible curves. */
3142 }
3143
3145 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
3146
3147 /* Paste modifiers. */
3148 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
3149 FCurve *fcu = (FCurve *)ale->data;
3150 int tot;
3151
3152 tot = ANIM_fmodifiers_paste_from_buf(&fcu->modifiers, replace, fcu);
3153
3154 if (tot) {
3155 ale->update |= ANIM_UPDATE_DEPS;
3156 ok = true;
3157 }
3158 }
3159
3160 if (ok) {
3161 ANIM_animdata_update(&ac, &anim_data);
3162 }
3163 ANIM_animdata_freelist(&anim_data);
3164
3165 /* Successful or not? */
3166 if (ok) {
3167 /* Set notifier that keyframes have changed. */
3169
3170 return OPERATOR_FINISHED;
3171 }
3172
3173 BKE_report(op->reports, RPT_ERROR, "No F-Modifiers to paste");
3174 return OPERATOR_CANCELLED;
3175}
3176
3178{
3179 /* Identifiers */
3180 ot->name = "Paste F-Modifiers";
3181 ot->idname = "GRAPH_OT_fmodifier_paste";
3182 ot->description = "Add copied F-Modifiers to the selected F-Curves";
3183
3184 /* API callbacks */
3187
3188 /* Flags */
3190
3191 /* Properties */
3193 ot->srna, "only_active", false, "Only Active", "Only paste F-Modifiers on active F-Curve");
3195 ot->srna,
3196 "replace",
3197 false,
3198 "Replace Existing",
3199 "Replace existing F-Modifiers, instead of just appending to the end of the existing list");
3200}
3201
3204/* ************************************************************************** */
3205/* Drivers */
3206
3207/* -------------------------------------------------------------------- */
3212{
3213 bool ok = false;
3214
3215 PointerRNA ptr = CTX_data_pointer_get_type(C, "active_editable_fcurve", &RNA_FCurve);
3216
3217 /* If this exists, call the copy driver vars API function. */
3218 FCurve *fcu = static_cast<FCurve *>(ptr.data);
3219
3220 if (fcu) {
3221 ok = ANIM_driver_vars_copy(op->reports, fcu);
3222 }
3223
3224 /* Successful or not? */
3225 if (ok) {
3226 return OPERATOR_FINISHED;
3227 }
3228 return OPERATOR_CANCELLED;
3229}
3230
3232{
3233 /* Identifiers */
3234 ot->name = "Copy Driver Variables";
3235 ot->idname = "GRAPH_OT_driver_variables_copy";
3236 ot->description = "Copy the driver variables of the active driver";
3237
3238 /* API callbacks */
3241
3242 /* Flags */
3244}
3245
3248/* -------------------------------------------------------------------- */
3253{
3254 const bool replace = RNA_boolean_get(op->ptr, "replace");
3255 bool ok = false;
3256
3257 PointerRNA ptr = CTX_data_pointer_get_type(C, "active_editable_fcurve", &RNA_FCurve);
3258
3259 /* If this exists, call the paste driver vars API function. */
3260 FCurve *fcu = static_cast<FCurve *>(ptr.data);
3261
3262 if (fcu) {
3263 ok = ANIM_driver_vars_paste(op->reports, fcu, replace);
3264 }
3265
3266 /* Successful or not? */
3267 if (ok) {
3268 /* Rebuild depsgraph, now that there are extra dependencies here. */
3270
3271 /* Set notifier that keyframes have changed. */
3273
3274 return OPERATOR_FINISHED;
3275 }
3276 return OPERATOR_CANCELLED;
3277}
3278
3280{
3281 /* Identifiers */
3282 ot->name = "Paste Driver Variables";
3283 ot->idname = "GRAPH_OT_driver_variables_paste";
3284 ot->description = "Add copied driver variables to the active driver";
3285
3286 /* API callbacks */
3289
3290 /* Flags */
3292
3293 /* Properties */
3295 "replace",
3296 false,
3297 "Replace Existing",
3298 "Replace existing driver variables, instead of just appending to the end of the "
3299 "existing list");
3300}
3301
3304/* -------------------------------------------------------------------- */
3309{
3310 bAnimContext ac;
3311 ListBase anim_data = {nullptr, nullptr};
3312 int filter;
3313 bool ok = false;
3314 uint deleted = 0;
3315
3316 /* Get editor data. */
3317 if (ANIM_animdata_get_context(C, &ac) == 0) {
3318 return OPERATOR_CANCELLED;
3319 }
3320
3321 /* NOTE: We might need a scene update to evaluate the driver flags. */
3322
3323 /* Filter data. */
3327 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
3328
3329 /* Find invalid drivers. */
3330 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
3331 FCurve *fcu = (FCurve *)ale->data;
3332 if (ELEM(nullptr, fcu, fcu->driver)) {
3333 continue;
3334 }
3335 if (!(fcu->driver->flag & DRIVER_FLAG_INVALID)) {
3336 continue;
3337 }
3338
3339 ok |= ANIM_remove_driver(ale->id, fcu->rna_path, fcu->array_index);
3340 if (!ok) {
3341 break;
3342 }
3343 deleted += 1;
3344 }
3345
3346 /* Cleanup. */
3347 ANIM_animdata_freelist(&anim_data);
3348
3349 if (deleted > 0) {
3350 /* Notify the world of any changes. */
3353 WM_reportf(RPT_INFO, "Deleted %u drivers", deleted);
3354 }
3355 else {
3356 WM_report(RPT_INFO, "No drivers deleted");
3357 }
3358
3359 /* Successful or not? */
3360 if (!ok) {
3361 return OPERATOR_CANCELLED;
3362 }
3363
3364 return OPERATOR_FINISHED;
3365}
3366
3368{
3369 bAnimContext ac;
3370 ScrArea *area = CTX_wm_area(C);
3371
3372 /* Firstly, check if in Graph Editor. */
3373 if ((area == nullptr) || (area->spacetype != SPACE_GRAPH)) {
3374 return false;
3375 }
3376
3377 /* Try to init Anim-Context stuff ourselves and check. */
3378 return ANIM_animdata_get_context(C, &ac) != 0;
3379}
3380
3382{
3383 /* Identifiers */
3384 ot->name = "Delete Invalid Drivers";
3385 ot->idname = "GRAPH_OT_driver_delete_invalid";
3386 ot->description = "Delete all visible drivers considered invalid";
3387
3388 /* API callbacks */
3391
3392 /* Flags */
3394}
3395
Functions to work with AnimData.
Functions to modify FCurves.
Functions to insert, delete or modify keyframes.
AnimationEvalContext BKE_animsys_eval_context_construct(struct Depsgraph *depsgraph, float eval_time) ATTR_WARN_UNUSED_RESULT
Definition anim_sys.cc:734
PointerRNA CTX_data_pointer_get_type(const bContext *C, const char *member, StructRNA *type)
ScrArea * CTX_wm_area(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
bool BKE_fcurve_is_keyframable(const FCurve *fcu)
const FModifierTypeInfo * get_fmodifier_typeinfo(int type)
int BKE_fcurve_bezt_binarysearch_index(const BezTriple array[], float frame, int arraylen, bool *r_replace)
void BKE_fcurve_handles_recalc(FCurve *fcu)
void fcurve_samples_to_keyframes(FCurve *fcu, int start, int end)
FModifier * add_fmodifier(ListBase *modifiers, int type, FCurve *owner_fcu)
bool remove_fmodifier(ListBase *modifiers, FModifier *fcm)
bool list_has_suitable_fmodifier(const ListBase *modifiers, int mtype, short acttype)
void set_active_fmodifier(ListBase *modifiers, FModifier *fcm)
float evaluate_fcurve_only_curve(const FCurve *fcu, float evaltime)
bool BKE_fcurve_is_empty(const FCurve *fcu)
bool BKE_fcurve_delete_keys_selected(FCurve *fcu)
void BKE_fcurve_keyframe_move_value_with_handles(BezTriple *keyframe, float new_value)
void BKE_fcurve_merge_duplicate_keys(FCurve *fcu, const int sel_flag, const bool use_handle)
bool BKE_fcurve_has_selected_control_points(const FCurve *fcu)
void fcurve_store_samples(FCurve *fcu, void *data, int start, int end, FcuSampleFunc sample_cb)
float fcurve_samplingcb_evalcurve(FCurve *fcu, void *data, float evaltime)
float BKE_nla_tweakedit_remap(AnimData *adt, float cframe, short mode)
@ NLATIME_CONVERT_UNMAP
Definition BKE_nla.hh:513
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
void BKE_scene_frame_set(Scene *scene, float frame)
Definition scene.cc:2336
float BKE_scene_frame_get(const Scene *scene)
Definition scene.cc:2331
#define BLI_assert(a)
Definition BLI_assert.h:50
bool BLI_is_file(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:438
void BLI_kdtree_nd_ free(KDTree *tree)
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
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
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE int round_fl_to_int(float a)
MINLINE int min_ii(int a, int b)
#define M_PI
void mat3_normalized_to_compatible_eul(float eul[3], const float oldrot[3], float mat[3][3])
void eul_to_mat3(float mat[3][3], const float eul[3])
#define FILE_MAX
unsigned int uint
#define ELEM(...)
#define STREQ(a, b)
#define BLT_I18NCONTEXT_ID_ACTION
#define RPT_(msgid)
#define TIP_(msgid)
#define IFACE_(msgid)
void DEG_relations_tag_update(Main *bmain)
eInsertKeyFlags
@ FMODIFIER_TYPE_CYCLES
@ FMODIFIER_NUM_TYPES
@ DRIVER_FLAG_INVALID
@ FCURVE_PROTECTED
@ FCURVE_EXTRAPOLATE_CONSTANT
@ FCURVE_EXTRAPOLATE_LINEAR
@ BEZT_FLAG_TEMP_TAG
eBezTriple_KeyframeType
#define PSFRA
#define FPS
#define PEFRA
@ FILE_SORT_DEFAULT
@ FILE_SPECIAL
@ FILE_TYPE_MOVIE
@ FILE_TYPE_SOUND
@ FILE_TYPE_FOLDER
@ SPACE_GRAPH
@ SIPO_MODE_DRIVERS
@ SIPO_NOHANDLES
@ FILE_DEFAULTDISPLAY
@ USER_ANIM_ONLY_SHOW_SELECTED_CURVE_KEYS
#define ANIM_UPDATE_DEFAULT_NOHANDLES
#define ANIM_UPDATE_DEFAULT
@ ANIM_UPDATE_DEPS
@ ANIM_UPDATE_HANDLES
eAnimCont_Types
eAnimFilter_Flags
@ ANIMFILTER_ACTIVE
@ ANIMFILTER_FOREDIT
@ ANIMFILTER_DATA_VISIBLE
@ ANIMFILTER_CURVE_VISIBLE
@ ANIMFILTER_NODUPLIS
@ ANIMFILTER_FCURVESONLY
@ ANIMFILTER_SEL
@ ANIM_UNITCONV_ONLYKEYS
@ ANIM_UNITCONV_RESTORE
@ MIRROR_KEYS_TIME
eEditKeyframes_Equalize
@ KEYFRAME_PASTE_MERGE_MIX
eKeyPasteValueOffset
@ KEYFRAME_PASTE_VALUE_OFFSET_NONE
eKeyPasteOffset
@ KEYFRAME_PASTE_OFFSET_CFRA_START
@ BEZT_OK_SELECTED
@ SNAP_KEYS_TIME
short(*)(KeyframeEditData *ked, BezTriple *bezt) KeyframeEditFunc
@ SELECT_SUBTRACT
@ KEYFRAME_PASTE_NOTHING_TO_PASTE
@ KEYFRAME_PASTE_OK
@ KEYFRAME_PASTE_NOWHERE_TO_PASTE
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
@ TFM_TRANSLATION
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ ALERT_ICON_NONE
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1663
@ WM_FILESEL_SHOW_PROPS
Definition WM_api.hh:939
@ WM_FILESEL_FILEPATH
Definition WM_api.hh:936
@ FILE_OPENFILE
Definition WM_api.hh:945
@ OPTYPE_UNDO_GROUPED
Definition WM_types.hh:187
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_ANIMATION
Definition WM_types.hh:355
#define NC_SCENE
Definition WM_types.hh:345
#define NA_ADDED
Definition WM_types.hh:552
#define NA_EDITED
Definition WM_types.hh:550
#define ND_KEYFRAME_PROP
Definition WM_types.hh:462
#define ND_FRAME
Definition WM_types.hh:401
#define NA_REMOVED
Definition WM_types.hh:553
#define ND_KEYFRAME
Definition WM_types.hh:461
void ANIM_animdata_freelist(ListBase *anim_data)
Definition anim_deps.cc:457
void ANIM_deselect_keys_in_animation_editors(bContext *C)
Definition anim_deps.cc:472
void ANIM_animdata_update(bAnimContext *ac, ListBase *anim_data)
Definition anim_deps.cc:350
short ANIM_get_normalization_flags(SpaceLink *space_link)
Definition anim_draw.cc:320
AnimData * ANIM_nla_mapping_get(bAnimContext *ac, bAnimListElem *ale)
Definition anim_draw.cc:210
void ANIM_nla_mapping_apply_fcurve(AnimData *adt, FCurve *fcu, bool restore, bool only_keys)
Definition anim_draw.cc:290
float ANIM_unit_mapping_get_factor(Scene *scene, ID *id, FCurve *fcu, short flag, float *r_offset)
Definition anim_draw.cc:529
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)
TimeMarker * ED_markers_get_first_selected(ListBase *markers)
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
float evaltime
unsigned int U
Definition btGjkEpa3.h:78
SIMD_FORCE_INLINE btScalar length() const
Return the length of the vector.
Definition btVector3.h:257
void generate_reports(ReportList *reports, eReportType report_level=RPT_ERROR)
bool ANIM_driver_vars_copy(ReportList *reports, FCurve *fcu)
Definition drivers.cc:708
bool ANIM_driver_vars_paste(ReportList *reports, FCurve *fcu, bool replace)
Definition drivers.cc:730
bool ANIM_remove_driver(ID *id, const char rna_path[], int array_index)
Main Driver Management API calls.
Definition drivers.cc:523
draw_view in_light_buf[] float
bool ANIM_fmodifiers_paste_from_buf(ListBase *modifiers, bool replace, FCurve *curve)
void ANIM_fmodifiers_copybuf_free()
bool ANIM_fmodifiers_copy_to_buf(ListBase *modifiers, bool active)
static int graph_fmodifier_paste_exec(bContext *C, wmOperator *op)
static const EnumPropertyItem prop_graphkeys_insertkey_types[]
Definition graph_edit.cc:77
void GRAPH_OT_delete(wmOperatorType *ot)
static void insert_graph_keys(bAnimContext *ac, eGraphKeys_InsertKey_Types mode)
void GRAPH_OT_click_insert(wmOperatorType *ot)
void GRAPH_OT_fmodifier_paste(wmOperatorType *ot)
void GRAPH_OT_smooth(wmOperatorType *ot)
static const EnumPropertyItem prop_graphkeys_equalize_handles_sides[]
static int graphkeys_smooth_exec(bContext *C, wmOperator *)
static void clean_graph_keys(bAnimContext *ac, float thresh, bool clean_chan)
static int graphkeys_sound_to_samples_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void mirror_graph_keys(bAnimContext *ac, short mode)
static KeyframeEditData sum_selected_keyframes(bAnimContext *ac)
static int graphkeys_bake_exec(bContext *C, wmOperator *)
static int graphkeys_delete_exec(bContext *C, wmOperator *)
void GRAPH_OT_equalize_handles(wmOperatorType *ot)
static int graphkeys_easing_exec(bContext *C, wmOperator *op)
static int graphkeys_expo_exec(bContext *C, wmOperator *op)
static bool euler_filter_multi_channel(tEulerFilter *euf, ReportList *reports)
void GRAPH_OT_keyframe_jump(wmOperatorType *ot)
static bool graph_driver_delete_invalid_poll(bContext *C)
static int graphkeys_insertkey_exec(bContext *C, wmOperator *op)
void GRAPH_OT_snap_cursor_value(wmOperatorType *ot)
static void euler_filter_perform_filter(ListBase *eulers, ReportList *reports, int *r_curves_filtered, int *r_curves_seen)
static int graphkeys_click_insert_exec(bContext *C, wmOperator *op)
static void sethandles_graph_keys(bAnimContext *ac, short mode)
static int graphkeys_selected_control_points_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void GRAPH_OT_bake_keys(wmOperatorType *ot)
static bool euler_filter_single_channel(FCurve *fcu)
static int graph_driver_delete_invalid_exec(bContext *C, wmOperator *)
static int graphkeys_mirror_exec(bContext *C, wmOperator *op)
static bool keyframe_time_differs(BezTriple *keyframes[3])
void GRAPH_OT_mirror(wmOperatorType *ot)
static void bake_graph_keys(bAnimContext *ac)
static bool graph_has_selected_control_points(bContext *C)
static void snap_graph_keys(bAnimContext *ac, short mode)
void GRAPH_OT_keys_to_samples(wmOperatorType *ot)
void GRAPH_OT_fmodifier_copy(wmOperatorType *ot)
void GRAPH_OT_samples_to_keys(wmOperatorType *ot)
static const EnumPropertyItem prop_graphkeys_mirror_types[]
static int graphkeys_keys_to_samples_exec(bContext *C, wmOperator *)
void GRAPH_OT_handle_type(wmOperatorType *ot)
static std::string graphkeys_paste_get_description(bContext *, wmOperatorType *, PointerRNA *ptr)
static void equalize_graph_keys(bAnimContext *ac, int mode, float handle_length, bool flatten)
void GRAPH_OT_frame_jump(wmOperatorType *ot)
void GRAPH_OT_driver_variables_copy(wmOperatorType *ot)
void GRAPH_OT_extrapolation_type(wmOperatorType *ot)
static bool delete_graph_keys(bAnimContext *ac)
#define MAKE_CYCLIC_EXPO
static int graphkeys_ipo_exec(bContext *C, wmOperator *op)
void GRAPH_OT_sound_to_samples(wmOperatorType *ot)
static int graphkeys_framejump_exec(bContext *C, wmOperator *)
static int graphkeys_sound_to_samples_exec(bContext *, wmOperator *op)
static int graphkeys_samples_to_keys_exec(bContext *C, wmOperator *)
void GRAPH_OT_driver_variables_paste(wmOperatorType *ot)
static int graphkeys_delete_invoke(bContext *C, wmOperator *op, const wmEvent *)
void GRAPH_OT_interpolation_type(wmOperatorType *ot)
static int graph_driver_vars_copy_exec(bContext *C, wmOperator *op)
static void setexpo_graph_keys(bAnimContext *ac, short mode)
eGraphKeys_InsertKey_Types
Definition graph_edit.cc:69
@ GRAPHKEYS_INSERTKEY_CURSOR
Definition graph_edit.cc:72
@ GRAPHKEYS_INSERTKEY_ACTIVE
Definition graph_edit.cc:73
@ GRAPHKEYS_INSERTKEY_ALL
Definition graph_edit.cc:70
@ GRAPHKEYS_INSERTKEY_SEL
Definition graph_edit.cc:71
static int graphkeys_equalize_handles_exec(bContext *C, wmOperator *op)
static void convert_keys_to_samples(bAnimContext *ac, int start, int end)
static const EnumPropertyItem prop_graphkeys_expo_types[]
static int graph_fmodifier_copy_exec(bContext *C, wmOperator *op)
static const EnumPropertyItem * graph_fmodifier_itemf(bContext *C, PointerRNA *, PropertyRNA *, bool *r_free)
static bool find_closest_frame(const FCurve *fcu, const float frame, const bool next, float *r_closest_frame)
static int graphkeys_click_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void GRAPH_OT_easing_type(wmOperatorType *ot)
static int graph_driver_vars_paste_exec(bContext *C, wmOperator *op)
static int graphkeys_euler_filter_exec(bContext *C, wmOperator *op)
static int graph_fmodifier_add_exec(bContext *C, wmOperator *op)
void GRAPH_OT_copy(wmOperatorType *ot)
static int graphkeys_paste_exec(bContext *C, wmOperator *op)
static void setipo_graph_keys(bAnimContext *ac, short mode)
void GRAPH_OT_driver_delete_invalid(wmOperatorType *ot)
static void convert_samples_to_keys(bAnimContext *ac, int start, int end)
void GRAPH_OT_keyframe_insert(wmOperatorType *ot)
static int graphkeys_copy_exec(bContext *C, wmOperator *op)
static eKeyPasteError paste_graph_keys(bAnimContext *ac, const eKeyPasteOffset offset_mode, const eKeyPasteValueOffset value_offset_mode, const eKeyMergeMode merge_mode, bool flip)
void GRAPH_OT_duplicate(wmOperatorType *ot)
static void seteasing_graph_keys(bAnimContext *ac, short mode)
static int graphkeys_handletype_exec(bContext *C, wmOperator *op)
void GRAPH_OT_euler_filter(wmOperatorType *ot)
static bool graphkeys_framejump_poll(bContext *C)
static int graphkeys_duplicate_exec(bContext *C, wmOperator *)
static short copy_graph_keys(bAnimContext *ac)
void GRAPH_OT_clean(wmOperatorType *ot)
static int graphkeys_clean_exec(bContext *C, wmOperator *op)
static int graphkeys_snap_exec(bContext *C, wmOperator *op)
#define CLEAR_CYCLIC_EXPO
void GRAPH_OT_snap(wmOperatorType *ot)
static int keyframe_jump_exec(bContext *C, wmOperator *op)
static bool duplicate_graph_keys(bAnimContext *ac)
static ListBase euler_filter_group_channels(const ListBase *anim_data, ReportList *reports, int *r_num_groups)
void GRAPH_OT_fmodifier_add(wmOperatorType *ot)
static const EnumPropertyItem prop_graphkeys_snap_types[]
static int graphkeys_snap_cursor_value_exec(bContext *C, wmOperator *)
void GRAPH_OT_paste(wmOperatorType *ot)
bool graphop_visible_keyframes_poll(bContext *C)
@ GRAPHKEYS_MIRROR_XAXIS
@ GRAPHKEYS_MIRROR_CFRA
@ GRAPHKEYS_MIRROR_VALUE
@ GRAPHKEYS_MIRROR_YAXIS
@ GRAPHKEYS_MIRROR_MARKER
bool graphop_active_editable_fcurve_ctx_poll(bContext *C)
bAnimListElem * get_active_fcurve_channel(bAnimContext *ac)
@ GRAPHKEYS_SNAP_NEAREST_MARKER
@ GRAPHKEYS_SNAP_VALUE
@ GRAPHKEYS_SNAP_NEAREST_SECOND
@ GRAPHKEYS_SNAP_HORIZONTAL
@ GRAPHKEYS_SNAP_CFRA
@ GRAPHKEYS_SNAP_NEAREST_FRAME
bool graphop_editable_keyframes_poll(bContext *C)
@ GRAPHKEYS_EQUALIZE_RIGHT
@ GRAPHKEYS_EQUALIZE_BOTH
@ GRAPHKEYS_EQUALIZE_LEFT
bool graphop_active_fcurve_poll(bContext *C)
void deselect_graph_keys(bAnimContext *ac, bool test, eEditKeyframes_Select sel, bool do_channels)
bool graphop_selected_fcurve_poll(bContext *C)
DO_INLINE void filter(lfVector *V, fmatrix3x3 *S)
KeyframeEditFunc ANIM_editkeyframes_mirror(short mode)
KeyframeEditFunc ANIM_editkeyframes_easing(short mode)
KeyframeEditFunc ANIM_editkeyframes_ipo(short mode)
void ANIM_fcurve_equalize_keyframes_loop(FCurve *fcu, const eEditKeyframes_Equalize mode, const float handle_length, const bool flatten)
short ANIM_fcurve_keyframes_loop(KeyframeEditData *ked, FCurve *fcu, KeyframeEditFunc key_ok, KeyframeEditFunc key_cb, FcuEditFunc fcu_cb)
KeyframeEditFunc ANIM_editkeyframes_ok(short mode)
KeyframeEditFunc ANIM_editkeyframes_snap(short mode)
short bezt_calc_average(KeyframeEditData *ked, BezTriple *bezt)
KeyframeEditFunc ANIM_editkeyframes_handles(short mode)
void clean_fcurve(bAnimContext *ac, bAnimListElem *ale, float thresh, bool cleardefault, const bool only_selected_keys)
const EnumPropertyItem rna_enum_keyframe_paste_offset_items[]
short copy_animedit_keys(bAnimContext *ac, ListBase *anim_data)
const EnumPropertyItem rna_enum_keyframe_paste_merge_items[]
eKeyPasteError paste_animedit_keys(bAnimContext *ac, ListBase *anim_data, const eKeyPasteOffset offset_mode, const eKeyPasteValueOffset value_offset_mode, const eKeyMergeMode merge_mode, bool flip)
const EnumPropertyItem rna_enum_keyframe_paste_offset_value_items[]
void ANIM_fcurves_copybuf_free()
void smooth_fcurve(FCurve *fcu)
bool duplicate_fcurve_keys(FCurve *fcu)
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
ccl_device_inline float2 fabs(const float2 a)
static ulong * next
#define G(x, y, z)
void bake_fcurve_segments(FCurve *fcu)
void animdata_fcurve_delete(bAnimContext *ac, AnimData *adt, FCurve *fcu)
Definition animdata.cc:253
eInsertKeyFlags get_keyframing_flags(Scene *scene)
void RNA_string_get(PointerRNA *ptr, const char *name, char *value)
float RNA_float_get(PointerRNA *ptr, const char *name)
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
int RNA_enum_from_value(const EnumPropertyItem *item, const int value)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PointerRNA RNA_id_pointer_create(ID *id)
const EnumPropertyItem rna_enum_beztriple_interpolation_mode_items[]
Definition rna_curve.cc:67
const EnumPropertyItem rna_enum_keyframe_handle_type_items[]
Definition rna_curve.cc:39
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)
void RNA_enum_item_end(EnumPropertyItem **items, int *totitem)
void RNA_enum_item_add(EnumPropertyItem **items, int *totitem, const EnumPropertyItem *item)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_translation_context(PropertyRNA *prop, const char *context)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
void RNA_def_enum_funcs(PropertyRNA *prop, EnumPropertyItemFunc itemfunc)
const EnumPropertyItem rna_enum_fmodifier_type_items[]
Definition rna_fcurve.cc:38
const EnumPropertyItem rna_enum_beztriple_interpolation_easing_items[]
#define FLT_MAX
Definition stdcycles.h:14
float vec[3][3]
bActionGroup * grp
char * rna_path
FPoint * fpt
ChannelDriver * driver
BezTriple * bezt
short extend
int array_index
unsigned int totvert
ListBase modifiers
struct FModifier * next
Definition DNA_ID.h:413
char name[66]
Definition DNA_ID.h:425
void * last
void * first
void * data
Definition RNA_types.hh:42
struct ToolSettings * toolsettings
SpaceLink * sl
ListBase * markers
eAnimCont_Types datatype
ReportList * reports
ARegion * region
Depsgraph * depsgraph
eAnim_Update_Flags update
tEulerFilter * next
const char * rna_path
FCurve * fcurves[3]
tEulerFilter * prev
const char * name
Definition WM_types.hh:990
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(* 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
PropertyRNA * prop
Definition WM_types.hh:1092
const char * undo_group
Definition WM_types.hh:998
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
struct PointerRNA * ptr
const EnumPropertyItem rna_enum_transform_mode_type_items[]
void WM_report(eReportType type, const char *message)
void WM_reportf(eReportType type, const char *format,...)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4126
wmOperatorType * ot
Definition wm_files.cc:4125
void WM_operator_properties_confirm_or_exec(wmOperatorType *ot)
void WM_operator_properties_filesel(wmOperatorType *ot, const int filter, const short type, const eFileSel_Action action, const eFileSel_Flag flag, const short display, const short sort)
int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
int WM_operator_confirm_ex(bContext *C, wmOperator *op, const char *title, const char *message, const char *confirm_text, int icon, bool cancel_default)
int WM_operator_filesel(bContext *C, wmOperator *op, const wmEvent *)
uint8_t flag
Definition wm_window.cc:138