1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19 
20 /** \file
21  * \ingroup spgraph
22  */
23 
24 #include <float.h>
25 #include <math.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #ifdef WITH_AUDASPACE
30 #  include <AUD_Special.h>
31 #endif
32 
33 #include "MEM_guardedalloc.h"
34 
35 #include "BLI_blenlib.h"
36 #include "BLI_math.h"
37 #include "BLI_utildefines.h"
38 
39 #include "DNA_anim_types.h"
40 #include "DNA_scene_types.h"
41 
42 #include "RNA_access.h"
43 #include "RNA_define.h"
44 #include "RNA_enum_types.h"
45 
46 #include "BLT_translation.h"
47 
48 #include "BKE_animsys.h"
49 #include "BKE_context.h"
50 #include "BKE_fcurve.h"
51 #include "BKE_global.h"
52 #include "BKE_nla.h"
53 #include "BKE_report.h"
54 
55 #include "DEG_depsgraph_build.h"
56 
57 #include "UI_interface.h"
58 #include "UI_view2d.h"
59 
60 #include "ED_anim_api.h"
61 #include "ED_keyframes_edit.h"
62 #include "ED_keyframing.h"
63 #include "ED_markers.h"
64 #include "ED_numinput.h"
65 #include "ED_screen.h"
66 #include "ED_transform.h"
67 
68 #include "WM_api.h"
69 #include "WM_types.h"
70 
71 #include "graph_intern.h"
72 
73 /* ************************************************************************** */
74 /* KEYFRAME-RANGE STUFF */
75 
76 /* *************************** Calculate Range ************************** */
77 
78 /* Get the min/max keyframes. */
79 /* Note: it should return total boundbox, filter for selection only can be argument... */
get_graph_keyframe_extents(bAnimContext * ac,float * xmin,float * xmax,float * ymin,float * ymax,const bool do_sel_only,const bool include_handles)80 void get_graph_keyframe_extents(bAnimContext *ac,
81                                 float *xmin,
82                                 float *xmax,
83                                 float *ymin,
84                                 float *ymax,
85                                 const bool do_sel_only,
86                                 const bool include_handles)
87 {
88   Scene *scene = ac->scene;
89   SpaceGraph *sipo = (SpaceGraph *)ac->sl;
90 
91   ListBase anim_data = {NULL, NULL};
92   bAnimListElem *ale;
93   int filter;
94 
95   /* Get data to filter, from Dopesheet. */
96   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
97   if (sipo->flag & SIPO_SELCUVERTSONLY) {
98     filter |= ANIMFILTER_SEL;
99   }
100 
101   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
102 
103   /* Set large values initial values that will be easy to override. */
104   if (xmin) {
105     *xmin = 999999999.0f;
106   }
107   if (xmax) {
108     *xmax = -999999999.0f;
109   }
110   if (ymin) {
111     *ymin = 999999999.0f;
112   }
113   if (ymax) {
114     *ymax = -999999999.0f;
115   }
116 
117   /* Check if any channels to set range with. */
118   if (anim_data.first) {
119     bool foundBounds = false;
120 
121     /* Go through channels, finding max extents. */
122     for (ale = anim_data.first; ale; ale = ale->next) {
123       AnimData *adt = ANIM_nla_mapping_get(ac, ale);
124       FCurve *fcu = (FCurve *)ale->key_data;
125       float txmin, txmax, tymin, tymax;
126       float unitFac, offset;
127 
128       /* Get range. */
129       if (BKE_fcurve_calc_bounds(
130               fcu, &txmin, &txmax, &tymin, &tymax, do_sel_only, include_handles)) {
131         short mapping_flag = ANIM_get_normalization_flags(ac);
132 
133         /* Apply NLA scaling. */
134         if (adt) {
135           txmin = BKE_nla_tweakedit_remap(adt, txmin, NLATIME_CONVERT_MAP);
136           txmax = BKE_nla_tweakedit_remap(adt, txmax, NLATIME_CONVERT_MAP);
137         }
138 
139         /* Apply unit corrections. */
140         unitFac = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag, &offset);
141         tymin += offset;
142         tymax += offset;
143         tymin *= unitFac;
144         tymax *= unitFac;
145 
146         /* Try to set cur using these values, if they're more extreme than previously set values.
147          */
148         if ((xmin) && (txmin < *xmin)) {
149           *xmin = txmin;
150         }
151         if ((xmax) && (txmax > *xmax)) {
152           *xmax = txmax;
153         }
154         if ((ymin) && (tymin < *ymin)) {
155           *ymin = tymin;
156         }
157         if ((ymax) && (tymax > *ymax)) {
158           *ymax = tymax;
159         }
160 
161         foundBounds = true;
162       }
163     }
164 
165     /* Ensure that the extents are not too extreme that view implodes...*/
166     if (foundBounds) {
167       if ((xmin && xmax) && (fabsf(*xmax - *xmin) < 0.001f)) {
168         *xmin -= 0.0005f;
169         *xmax += 0.0005f;
170       }
171       if ((ymin && ymax) && (fabsf(*ymax - *ymin) < 0.001f)) {
172         *ymax -= 0.0005f;
173         *ymax += 0.0005f;
174       }
175     }
176     else {
177       if (xmin) {
178         *xmin = (float)PSFRA;
179       }
180       if (xmax) {
181         *xmax = (float)PEFRA;
182       }
183       if (ymin) {
184         *ymin = -5;
185       }
186       if (ymax) {
187         *ymax = 5;
188       }
189     }
190 
191     /* Free memory. */
192     ANIM_animdata_freelist(&anim_data);
193   }
194   else {
195     /* Set default range. */
196     if (ac->scene) {
197       if (xmin) {
198         *xmin = (float)PSFRA;
199       }
200       if (xmax) {
201         *xmax = (float)PEFRA;
202       }
203     }
204     else {
205       if (xmin) {
206         *xmin = -5;
207       }
208       if (xmax) {
209         *xmax = 100;
210       }
211     }
212 
213     if (ymin) {
214       *ymin = -5;
215     }
216     if (ymax) {
217       *ymax = 5;
218     }
219   }
220 }
221 
222 /* ****************** Automatic Preview-Range Operator ****************** */
223 
graphkeys_previewrange_exec(bContext * C,wmOperator * UNUSED (op))224 static int graphkeys_previewrange_exec(bContext *C, wmOperator *UNUSED(op))
225 {
226   bAnimContext ac;
227   Scene *scene;
228   float min, max;
229 
230   /* Get editor data. */
231   if (ANIM_animdata_get_context(C, &ac) == 0) {
232     return OPERATOR_CANCELLED;
233   }
234   if (ac.scene == NULL) {
235     return OPERATOR_CANCELLED;
236   }
237 
238   scene = ac.scene;
239 
240   /* Set the range directly. */
241   get_graph_keyframe_extents(&ac, &min, &max, NULL, NULL, false, false);
242   scene->r.flag |= SCER_PRV_RANGE;
243   scene->r.psfra = round_fl_to_int(min);
244   scene->r.pefra = round_fl_to_int(max);
245 
246   /* Set notifier that things have changed. */
247   // XXX Err... there's nothing for frame ranges yet, but this should do fine too.
248   WM_event_add_notifier(C, NC_SCENE | ND_FRAME, ac.scene);
249 
250   return OPERATOR_FINISHED;
251 }
252 
GRAPH_OT_previewrange_set(wmOperatorType * ot)253 void GRAPH_OT_previewrange_set(wmOperatorType *ot)
254 {
255   /* Identifiers */
256   ot->name = "Auto-Set Preview Range";
257   ot->idname = "GRAPH_OT_previewrange_set";
258   ot->description = "Automatically set Preview Range based on range of keyframes";
259 
260   /* API callbacks */
261   ot->exec = graphkeys_previewrange_exec;
262   /* XXX: unchecked poll to get fsamples working too, but makes modifier damage trickier. */
263   ot->poll = ED_operator_graphedit_active;
264 
265   /* Flags */
266   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
267 }
268 
269 /* ****************** View-All Operator ****************** */
270 
graphkeys_viewall(bContext * C,const bool do_sel_only,const bool include_handles,const int smooth_viewtx)271 static int graphkeys_viewall(bContext *C,
272                              const bool do_sel_only,
273                              const bool include_handles,
274                              const int smooth_viewtx)
275 {
276   bAnimContext ac;
277   rctf cur_new;
278 
279   /* Get editor data. */
280   if (ANIM_animdata_get_context(C, &ac) == 0) {
281     return OPERATOR_CANCELLED;
282   }
283 
284   /* Set the horizontal range, with an extra offset so that the extreme keys will be in view. */
285   get_graph_keyframe_extents(&ac,
286                              &cur_new.xmin,
287                              &cur_new.xmax,
288                              &cur_new.ymin,
289                              &cur_new.ymax,
290                              do_sel_only,
291                              include_handles);
292 
293   /* Give some more space at the borders. */
294   BLI_rctf_scale(&cur_new, 1.1f);
295 
296   /* Take regions into account, that could block the view.
297    * Marker region is supposed to be larger than the scroll-bar, so prioritize it.*/
298   float pad_top = UI_TIME_SCRUB_MARGIN_Y;
299   float pad_bottom = BLI_listbase_is_empty(ED_context_get_markers(C)) ? V2D_SCROLL_HANDLE_HEIGHT :
300                                                                         UI_MARKER_MARGIN_Y;
301   BLI_rctf_pad_y(&cur_new, ac.region->winy, pad_bottom, pad_top);
302 
303   UI_view2d_smooth_view(C, ac.region, &cur_new, smooth_viewtx);
304   return OPERATOR_FINISHED;
305 }
306 
307 /* ......... */
308 
graphkeys_viewall_exec(bContext * C,wmOperator * op)309 static int graphkeys_viewall_exec(bContext *C, wmOperator *op)
310 {
311   const bool include_handles = RNA_boolean_get(op->ptr, "include_handles");
312   const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
313 
314   /* Whole range */
315   return graphkeys_viewall(C, false, include_handles, smooth_viewtx);
316 }
317 
graphkeys_view_selected_exec(bContext * C,wmOperator * op)318 static int graphkeys_view_selected_exec(bContext *C, wmOperator *op)
319 {
320   const bool include_handles = RNA_boolean_get(op->ptr, "include_handles");
321   const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
322 
323   /* Only selected. */
324   return graphkeys_viewall(C, true, include_handles, smooth_viewtx);
325 }
326 
327 /* ......... */
328 
GRAPH_OT_view_all(wmOperatorType * ot)329 void GRAPH_OT_view_all(wmOperatorType *ot)
330 {
331   /* Identifiers */
332   ot->name = "Frame All";
333   ot->idname = "GRAPH_OT_view_all";
334   ot->description = "Reset viewable area to show full keyframe range";
335 
336   /* API callbacks */
337   ot->exec = graphkeys_viewall_exec;
338   /* XXX: Unchecked poll to get fsamples working too, but makes modifier damage trickier... */
339   ot->poll = ED_operator_graphedit_active;
340 
341   /* Flags */
342   ot->flag = 0;
343 
344   /* Props */
345   ot->prop = RNA_def_boolean(ot->srna,
346                              "include_handles",
347                              true,
348                              "Include Handles",
349                              "Include handles of keyframes when calculating extents");
350 }
351 
GRAPH_OT_view_selected(wmOperatorType * ot)352 void GRAPH_OT_view_selected(wmOperatorType *ot)
353 {
354   /* Identifiers */
355   ot->name = "Frame Selected";
356   ot->idname = "GRAPH_OT_view_selected";
357   ot->description = "Reset viewable area to show selected keyframe range";
358 
359   /* API callbacks */
360   ot->exec = graphkeys_view_selected_exec;
361   /* XXX: Unchecked poll to get fsamples working too, but makes modifier damage trickier... */
362   ot->poll = ED_operator_graphedit_active;
363 
364   /* Flags */
365   ot->flag = 0;
366 
367   /* Props */
368   ot->prop = RNA_def_boolean(ot->srna,
369                              "include_handles",
370                              true,
371                              "Include Handles",
372                              "Include handles of keyframes when calculating extents");
373 }
374 
375 /* ********************** View Frame Operator ****************************** */
376 
graphkeys_view_frame_exec(bContext * C,wmOperator * op)377 static int graphkeys_view_frame_exec(bContext *C, wmOperator *op)
378 {
379   const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
380   ANIM_center_frame(C, smooth_viewtx);
381   return OPERATOR_FINISHED;
382 }
383 
GRAPH_OT_view_frame(wmOperatorType * ot)384 void GRAPH_OT_view_frame(wmOperatorType *ot)
385 {
386   /* Identifiers */
387   ot->name = "Go to Current Frame";
388   ot->idname = "GRAPH_OT_view_frame";
389   ot->description = "Move the view to the current frame";
390 
391   /* API callbacks */
392   ot->exec = graphkeys_view_frame_exec;
393   ot->poll = ED_operator_graphedit_active;
394 
395   /* Flags */
396   ot->flag = 0;
397 }
398 
399 /* ******************** Create Ghost-Curves Operator *********************** */
400 /* This operator samples the data of the selected F-Curves to F-Points, storing them
401  * as 'ghost curves' in the active Graph Editor.
402  */
403 
404 /* Bake each F-Curve into a set of samples, and store as a ghost curve. */
create_ghost_curves(bAnimContext * ac,int start,int end)405 static void create_ghost_curves(bAnimContext *ac, int start, int end)
406 {
407   SpaceGraph *sipo = (SpaceGraph *)ac->sl;
408   ListBase anim_data = {NULL, NULL};
409   bAnimListElem *ale;
410   int filter;
411 
412   /* Free existing ghost curves. */
413   BKE_fcurves_free(&sipo->runtime.ghost_curves);
414 
415   /* Sanity check. */
416   if (start >= end) {
417     printf("Error: Frame range for Ghost F-Curve creation is inappropriate\n");
418     return;
419   }
420 
421   /* Filter data. */
422   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL |
423             ANIMFILTER_NODUPLIS);
424   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
425 
426   /* Loop through filtered data and add keys between selected keyframes on every frame . */
427   for (ale = anim_data.first; ale; ale = ale->next) {
428     FCurve *fcu = (FCurve *)ale->key_data;
429     FCurve *gcu = BKE_fcurve_create();
430     AnimData *adt = ANIM_nla_mapping_get(ac, ale);
431     ChannelDriver *driver = fcu->driver;
432     FPoint *fpt;
433     float unitFac, offset;
434     int cfra;
435     short mapping_flag = ANIM_get_normalization_flags(ac);
436 
437     /* Disable driver so that it don't muck up the sampling process. */
438     fcu->driver = NULL;
439 
440     /* Calculate unit-mapping factor. */
441     unitFac = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag, &offset);
442 
443     /* Create samples, but store them in a new curve
444      * - we cannot use fcurve_store_samples() as that will only overwrite the original curve.
445      */
446     gcu->fpt = fpt = MEM_callocN(sizeof(FPoint) * (end - start + 1), "Ghost FPoint Samples");
447     gcu->totvert = end - start + 1;
448 
449     /* Use the sampling callback at 1-frame intervals from start to end frames. */
450     for (cfra = start; cfra <= end; cfra++, fpt++) {
451       float cfrae = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP);
452 
453       fpt->vec[0] = cfrae;
454       fpt->vec[1] = (fcurve_samplingcb_evalcurve(fcu, NULL, cfrae) + offset) * unitFac;
455     }
456 
457     /* Set color of ghost curve
458      * - make the color slightly darker.
459      */
460     gcu->color[0] = fcu->color[0] - 0.07f;
461     gcu->color[1] = fcu->color[1] - 0.07f;
462     gcu->color[2] = fcu->color[2] - 0.07f;
463 
464     /* Store new ghost curve. */
465     BLI_addtail(&sipo->runtime.ghost_curves, gcu);
466 
467     /* Restore driver. */
468     fcu->driver = driver;
469   }
470 
471   /* Admin and redraws. */
472   ANIM_animdata_freelist(&anim_data);
473 }
474 
475 /* ------------------- */
476 
graphkeys_create_ghostcurves_exec(bContext * C,wmOperator * UNUSED (op))477 static int graphkeys_create_ghostcurves_exec(bContext *C, wmOperator *UNUSED(op))
478 {
479   bAnimContext ac;
480   View2D *v2d;
481   int start, end;
482 
483   /* Get editor data. */
484   if (ANIM_animdata_get_context(C, &ac) == 0) {
485     return OPERATOR_CANCELLED;
486   }
487 
488   /* Ghost curves are snapshots of the visible portions of the curves,
489    * so set range to be the visible range. */
490   v2d = &ac.region->v2d;
491   start = (int)v2d->cur.xmin;
492   end = (int)v2d->cur.xmax;
493 
494   /* Bake selected curves into a ghost curve. */
495   create_ghost_curves(&ac, start, end);
496 
497   /* Update this editor only. */
498   ED_area_tag_redraw(CTX_wm_area(C));
499 
500   return OPERATOR_FINISHED;
501 }
502 
GRAPH_OT_ghost_curves_create(wmOperatorType * ot)503 void GRAPH_OT_ghost_curves_create(wmOperatorType *ot)
504 {
505   /* Identifiers */
506   ot->name = "Create Ghost Curves";
507   ot->idname = "GRAPH_OT_ghost_curves_create";
508   ot->description =
509       "Create snapshot (Ghosts) of selected F-Curves as background aid for active Graph Editor";
510 
511   /* API callbacks */
512   ot->exec = graphkeys_create_ghostcurves_exec;
513   ot->poll = graphop_visible_keyframes_poll;
514 
515   /* Flags */
516   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
517 
518   /* TODO: add props for start/end frames */
519 }
520 
521 /* ******************** Clear Ghost-Curves Operator *********************** */
522 /* This operator clears the 'ghost curves' for the active Graph Editor */
523 
graphkeys_clear_ghostcurves_exec(bContext * C,wmOperator * UNUSED (op))524 static int graphkeys_clear_ghostcurves_exec(bContext *C, wmOperator *UNUSED(op))
525 {
526   bAnimContext ac;
527   SpaceGraph *sipo;
528 
529   /* Get editor data. */
530   if (ANIM_animdata_get_context(C, &ac) == 0) {
531     return OPERATOR_CANCELLED;
532   }
533   sipo = (SpaceGraph *)ac.sl;
534 
535   /* If no ghost curves, don't do anything. */
536   if (BLI_listbase_is_empty(&sipo->runtime.ghost_curves)) {
537     return OPERATOR_CANCELLED;
538   }
539   /* Free ghost curves. */
540   BKE_fcurves_free(&sipo->runtime.ghost_curves);
541 
542   /* Update this editor only. */
543   ED_area_tag_redraw(CTX_wm_area(C));
544 
545   return OPERATOR_FINISHED;
546 }
547 
GRAPH_OT_ghost_curves_clear(wmOperatorType * ot)548 void GRAPH_OT_ghost_curves_clear(wmOperatorType *ot)
549 {
550   /* Identifiers */
551   ot->name = "Clear Ghost Curves";
552   ot->idname = "GRAPH_OT_ghost_curves_clear";
553   ot->description = "Clear F-Curve snapshots (Ghosts) for active Graph Editor";
554 
555   /* API callbacks */
556   ot->exec = graphkeys_clear_ghostcurves_exec;
557   ot->poll = ED_operator_graphedit_active;
558 
559   /* Flags */
560   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
561 }
562 
563 /* ************************************************************************** */
564 /* GENERAL STUFF */
565 
566 /* ******************** Insert Keyframes Operator ************************* */
567 
568 /* Mode defines for insert keyframes tool. */
569 typedef enum eGraphKeys_InsertKey_Types {
570   GRAPHKEYS_INSERTKEY_ALL = (1 << 0),
571   GRAPHKEYS_INSERTKEY_SEL = (1 << 1),
572   GRAPHKEYS_INSERTKEY_CURSOR = (1 << 2),
573   GRAPHKEYS_INSERTKEY_ACTIVE = (1 << 3),
574 } eGraphKeys_InsertKey_Types;
575 
576 /* RNA mode types for insert keyframes tool. */
577 static const EnumPropertyItem prop_graphkeys_insertkey_types[] = {
578     {GRAPHKEYS_INSERTKEY_ALL,
579      "ALL",
580      0,
581      "All Channels",
582      "Insert a keyframe on all visible and editable F-Curves using each curve's current value"},
583     {GRAPHKEYS_INSERTKEY_SEL,
584      "SEL",
585      0,
586      "Only Selected Channels",
587      "Insert a keyframe on selected F-Curves using each curve's current value"},
588     {GRAPHKEYS_INSERTKEY_ACTIVE | GRAPHKEYS_INSERTKEY_CURSOR,
589      "CURSOR_ACTIVE",
590      0,
591      "Active Channels At Cursor",
592      "Insert a keyframe for the active F-Curve at the cursor point"},
593     {GRAPHKEYS_INSERTKEY_SEL | GRAPHKEYS_INSERTKEY_CURSOR,
594      "CURSOR_SEL",
595      0,
596      "Selected Channels At Cursor",
597      "Insert a keyframe for selected F-Curves at the cursor point"},
598     {0, NULL, 0, NULL, NULL},
599 };
600 
601 /* This function is responsible for snapping keyframes to frame-times. */
insert_graph_keys(bAnimContext * ac,eGraphKeys_InsertKey_Types mode)602 static void insert_graph_keys(bAnimContext *ac, eGraphKeys_InsertKey_Types mode)
603 {
604   ListBase anim_data = {NULL, NULL};
605   ListBase nla_cache = {NULL, NULL};
606   bAnimListElem *ale;
607   int filter;
608   size_t num_items;
609 
610   ReportList *reports = ac->reports;
611   SpaceGraph *sipo = (SpaceGraph *)ac->sl;
612   Scene *scene = ac->scene;
613   ToolSettings *ts = scene->toolsettings;
614   eInsertKeyFlags flag = 0;
615 
616   /* Filter data. */
617   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
618             ANIMFILTER_NODUPLIS);
619   if (mode & GRAPHKEYS_INSERTKEY_SEL) {
620     filter |= ANIMFILTER_SEL;
621   }
622   else if (mode & GRAPHKEYS_INSERTKEY_ACTIVE) {
623     filter |= ANIMFILTER_ACTIVE;
624   }
625 
626   num_items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
627   if (num_items == 0) {
628     if (mode & GRAPHKEYS_INSERTKEY_ACTIVE) {
629       BKE_report(reports,
630                  RPT_ERROR,
631                  "No active F-Curve to add a keyframe to. Select an editable F-Curve first");
632     }
633     else if (mode & GRAPHKEYS_INSERTKEY_SEL) {
634       BKE_report(reports, RPT_ERROR, "No selected F-Curves to add keyframes to");
635     }
636     else {
637       BKE_report(reports, RPT_ERROR, "No channels to add keyframes to");
638     }
639 
640     return;
641   }
642 
643   /* Init key-framing flag. */
644   flag = ANIM_get_keyframing_flags(scene, true);
645 
646   /* Insert keyframes. */
647   if (mode & GRAPHKEYS_INSERTKEY_CURSOR) {
648     for (ale = anim_data.first; ale; ale = ale->next) {
649       AnimData *adt = ANIM_nla_mapping_get(ac, ale);
650       FCurve *fcu = (FCurve *)ale->key_data;
651 
652       short mapping_flag = ANIM_get_normalization_flags(ac);
653       float offset;
654       float unit_scale = ANIM_unit_mapping_get_factor(
655           ac->scene, ale->id, ale->key_data, mapping_flag, &offset);
656 
657       float x, y;
658 
659       /* perform time remapping for x-coordinate (if necessary) */
660       if ((sipo) && (sipo->mode == SIPO_MODE_DRIVERS)) {
661         x = sipo->cursorTime;
662       }
663       else if (adt) {
664         x = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
665       }
666       else {
667         x = (float)CFRA;
668       }
669 
670       /* Normalise units of cursor's value. */
671       if (sipo) {
672         y = (sipo->cursorVal / unit_scale) - offset;
673       }
674       else {
675         y = 0.0f;
676       }
677 
678       /* Insert keyframe directly into the F-Curve. */
679       insert_vert_fcurve(fcu, x, y, ts->keyframe_type, 0);
680 
681       ale->update |= ANIM_UPDATE_DEFAULT;
682     }
683   }
684   else {
685     const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
686         ac->depsgraph, (float)CFRA);
687     for (ale = anim_data.first; ale; ale = ale->next) {
688       FCurve *fcu = (FCurve *)ale->key_data;
689 
690       /* Read value from property the F-Curve represents, or from the curve only?
691        *
692        * - ale->id != NULL:
693        *   Typically, this means that we have enough info to try resolving the path.
694        * - ale->owner != NULL:
695        *   If this is set, then the path may not be resolvable from the ID alone,
696        *   so it's easier for now to just read the F-Curve directly.
697        *   (TODO: add the full-blown PointerRNA relative parsing case here... (Joshua Leung 2015))
698        * - fcu->driver != NULL:
699        *   If this is set, then it's a driver. If we don't check for this, we'd end
700        *   up adding the keyframes on a new F-Curve in the action data instead.
701        */
702       if (ale->id && !ale->owner && !fcu->driver) {
703         insert_keyframe(ac->bmain,
704                         reports,
705                         ale->id,
706                         NULL,
707                         ((fcu->grp) ? (fcu->grp->name) : (NULL)),
708                         fcu->rna_path,
709                         fcu->array_index,
710                         &anim_eval_context,
711                         ts->keyframe_type,
712                         &nla_cache,
713                         flag);
714       }
715       else {
716         AnimData *adt = ANIM_nla_mapping_get(ac, ale);
717 
718         /* Adjust current frame for NLA-mapping. */
719         float cfra = (float)CFRA;
720         if ((sipo) && (sipo->mode == SIPO_MODE_DRIVERS)) {
721           cfra = sipo->cursorTime;
722         }
723         else if (adt) {
724           cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
725         }
726 
727         const float curval = evaluate_fcurve_only_curve(fcu, cfra);
728         insert_vert_fcurve(fcu, cfra, curval, ts->keyframe_type, 0);
729       }
730 
731       ale->update |= ANIM_UPDATE_DEFAULT;
732     }
733   }
734 
735   BKE_animsys_free_nla_keyframing_context_cache(&nla_cache);
736 
737   ANIM_animdata_update(ac, &anim_data);
738   ANIM_animdata_freelist(&anim_data);
739 }
740 
741 /* ------------------- */
742 
graphkeys_insertkey_exec(bContext * C,wmOperator * op)743 static int graphkeys_insertkey_exec(bContext *C, wmOperator *op)
744 {
745   bAnimContext ac;
746   eGraphKeys_InsertKey_Types mode;
747 
748   /* Get editor data. */
749   if (ANIM_animdata_get_context(C, &ac) == 0) {
750     return OPERATOR_CANCELLED;
751   }
752 
753   /* Which channels to affect?. */
754   mode = RNA_enum_get(op->ptr, "type");
755 
756   /* Insert keyframes. */
757   insert_graph_keys(&ac, mode);
758 
759   /* Set notifier that keyframes have changed. */
760   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, NULL);
761 
762   return OPERATOR_FINISHED;
763 }
764 
GRAPH_OT_keyframe_insert(wmOperatorType * ot)765 void GRAPH_OT_keyframe_insert(wmOperatorType *ot)
766 {
767   /* Identifiers */
768   ot->name = "Insert Keyframes";
769   ot->idname = "GRAPH_OT_keyframe_insert";
770   ot->description = "Insert keyframes for the specified channels";
771 
772   /* API callbacks */
773   ot->invoke = WM_menu_invoke;
774   ot->exec = graphkeys_insertkey_exec;
775   ot->poll = graphop_editable_keyframes_poll;
776 
777   /* Flags */
778   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
779 
780   /* Id-props */
781   ot->prop = RNA_def_enum(ot->srna, "type", prop_graphkeys_insertkey_types, 0, "Type", "");
782 }
783 
784 /* ******************** Click-Insert Keyframes Operator ************************* */
785 
graphkeys_click_insert_exec(bContext * C,wmOperator * op)786 static int graphkeys_click_insert_exec(bContext *C, wmOperator *op)
787 {
788   bAnimContext ac;
789   bAnimListElem *ale;
790   AnimData *adt;
791   FCurve *fcu;
792   float frame, val;
793 
794   /* Get animation context. */
795   if (ANIM_animdata_get_context(C, &ac) == 0) {
796     return OPERATOR_CANCELLED;
797   }
798 
799   /* Get active F-Curve 'anim-list-element'. */
800   ale = get_active_fcurve_channel(&ac);
801   if (ELEM(NULL, ale, ale->data)) {
802     if (ale) {
803       MEM_freeN(ale);
804     }
805     return OPERATOR_CANCELLED;
806   }
807   fcu = ale->data;
808 
809   /* When there are F-Modifiers on the curve, only allow adding
810    * keyframes if these will be visible after doing so...
811    */
812   if (BKE_fcurve_is_keyframable(fcu)) {
813     ListBase anim_data;
814     ToolSettings *ts = ac.scene->toolsettings;
815 
816     short mapping_flag = ANIM_get_normalization_flags(&ac);
817     float scale, offset;
818 
819     /* Preserve selection? */
820     if (RNA_boolean_get(op->ptr, "extend") == false) {
821       /* Deselect all keyframes first,
822        * so that we can immediately start manipulating the newly added one(s)
823        * - only affect the keyframes themselves, as we don't want channels popping in and out. */
824       deselect_graph_keys(&ac, false, SELECT_SUBTRACT, false);
825     }
826 
827     /* Get frame and value from props. */
828     frame = RNA_float_get(op->ptr, "frame");
829     val = RNA_float_get(op->ptr, "value");
830 
831     /* Apply inverse NLA-mapping to frame to get correct time in un-scaled action. */
832     adt = ANIM_nla_mapping_get(&ac, ale);
833     frame = BKE_nla_tweakedit_remap(adt, frame, NLATIME_CONVERT_UNMAP);
834 
835     /* Apply inverse unit-mapping to value to get correct value for F-Curves. */
836     scale = ANIM_unit_mapping_get_factor(
837         ac.scene, ale->id, fcu, mapping_flag | ANIM_UNITCONV_RESTORE, &offset);
838 
839     val = val * scale - offset;
840 
841     /* Insert keyframe on the specified frame + value. */
842     insert_vert_fcurve(fcu, frame, val, ts->keyframe_type, 0);
843 
844     ale->update |= ANIM_UPDATE_DEPS;
845 
846     BLI_listbase_clear(&anim_data);
847     BLI_addtail(&anim_data, ale);
848 
849     ANIM_animdata_update(&ac, &anim_data);
850   }
851   else {
852     /* Warn about why this can't happen. */
853     if (fcu->fpt) {
854       BKE_report(op->reports, RPT_ERROR, "Keyframes cannot be added to sampled F-Curves");
855     }
856     else if (fcu->flag & FCURVE_PROTECTED) {
857       BKE_report(op->reports, RPT_ERROR, "Active F-Curve is not editable");
858     }
859     else {
860       BKE_report(op->reports, RPT_ERROR, "Remove F-Modifiers from F-Curve to add keyframes");
861     }
862   }
863 
864   /* Free temp data. */
865   MEM_freeN(ale);
866 
867   /* Set notifier that keyframes have changed. */
868   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
869 
870   /* Done */
871   return OPERATOR_FINISHED;
872 }
873 
graphkeys_click_insert_invoke(bContext * C,wmOperator * op,const wmEvent * event)874 static int graphkeys_click_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event)
875 {
876   bAnimContext ac;
877   ARegion *region;
878   View2D *v2d;
879   int mval[2];
880   float x, y;
881 
882   /* Get animation context. */
883   if (ANIM_animdata_get_context(C, &ac) == 0) {
884     return OPERATOR_CANCELLED;
885   }
886 
887   /* Store mouse coordinates in View2D space, into the operator's properties. */
888   region = ac.region;
889   v2d = &region->v2d;
890 
891   mval[0] = (event->x - region->winrct.xmin);
892   mval[1] = (event->y - region->winrct.ymin);
893 
894   UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y);
895 
896   RNA_float_set(op->ptr, "frame", x);
897   RNA_float_set(op->ptr, "value", y);
898 
899   /* Run exec now. */
900   return graphkeys_click_insert_exec(C, op);
901 }
902 
GRAPH_OT_click_insert(wmOperatorType * ot)903 void GRAPH_OT_click_insert(wmOperatorType *ot)
904 {
905   /* Identifiers */
906   ot->name = "Click-Insert Keyframes";
907   ot->idname = "GRAPH_OT_click_insert";
908   ot->description = "Insert new keyframe at the cursor position for the active F-Curve";
909 
910   /* API callbacks */
911   ot->invoke = graphkeys_click_insert_invoke;
912   ot->exec = graphkeys_click_insert_exec;
913   ot->poll = graphop_active_fcurve_poll;
914 
915   /* Flags */
916   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
917 
918   /* Properties */
919   RNA_def_float(ot->srna,
920                 "frame",
921                 1.0f,
922                 -FLT_MAX,
923                 FLT_MAX,
924                 "Frame Number",
925                 "Frame to insert keyframe on",
926                 0,
927                 100);
928   RNA_def_float(
929       ot->srna, "value", 1.0f, -FLT_MAX, FLT_MAX, "Value", "Value for keyframe on", 0, 100);
930 
931   RNA_def_boolean(ot->srna,
932                   "extend",
933                   false,
934                   "Extend",
935                   "Extend selection instead of deselecting everything first");
936 }
937 
938 /* ******************** Copy/Paste Keyframes Operator ************************* */
939 /* NOTE: the backend code for this is shared with the dopesheet editor */
940 
copy_graph_keys(bAnimContext * ac)941 static short copy_graph_keys(bAnimContext *ac)
942 {
943   ListBase anim_data = {NULL, NULL};
944   int filter, ok = 0;
945 
946   /* Clear buffer first. */
947   ANIM_fcurves_copybuf_free();
948 
949   /* Filter data
950    * - First time we try to filter more strictly, allowing only selected channels
951    *   to allow copying animation between channels.
952    */
953   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
954 
955   if (ANIM_animdata_filter(ac, &anim_data, filter | ANIMFILTER_SEL, ac->data, ac->datatype) == 0) {
956     ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
957   }
958 
959   /* Copy keyframes. */
960   ok = copy_animedit_keys(ac, &anim_data);
961 
962   /* Clean up. */
963   ANIM_animdata_freelist(&anim_data);
964 
965   return ok;
966 }
967 
paste_graph_keys(bAnimContext * ac,const eKeyPasteOffset offset_mode,const eKeyMergeMode merge_mode,bool flip)968 static short paste_graph_keys(bAnimContext *ac,
969                               const eKeyPasteOffset offset_mode,
970                               const eKeyMergeMode merge_mode,
971                               bool flip)
972 {
973   ListBase anim_data = {NULL, NULL};
974   int filter, ok = 0;
975 
976   /* Filter data
977    * - First time we try to filter more strictly, allowing only selected channels
978    *   to allow copying animation between channels
979    * - Second time, we loosen things up if nothing was found the first time, allowing
980    *   users to just paste keyframes back into the original curve again T31670.
981    */
982   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
983             ANIMFILTER_NODUPLIS);
984 
985   if (ANIM_animdata_filter(ac, &anim_data, filter | ANIMFILTER_SEL, ac->data, ac->datatype) == 0) {
986     ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
987   }
988 
989   /* Paste keyframes. */
990   ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode, flip);
991 
992   /* Clean up. */
993   ANIM_animdata_freelist(&anim_data);
994 
995   return ok;
996 }
997 
998 /* ------------------- */
999 
graphkeys_copy_exec(bContext * C,wmOperator * op)1000 static int graphkeys_copy_exec(bContext *C, wmOperator *op)
1001 {
1002   bAnimContext ac;
1003 
1004   /* Get editor data. */
1005   if (ANIM_animdata_get_context(C, &ac) == 0) {
1006     return OPERATOR_CANCELLED;
1007   }
1008 
1009   /* Copy keyframes. */
1010   if (copy_graph_keys(&ac)) {
1011     BKE_report(op->reports, RPT_ERROR, "No keyframes copied to keyframes copy/paste buffer");
1012     return OPERATOR_CANCELLED;
1013   }
1014 
1015   /* Just return - no operator needed here (no changes). */
1016   return OPERATOR_FINISHED;
1017 }
1018 
GRAPH_OT_copy(wmOperatorType * ot)1019 void GRAPH_OT_copy(wmOperatorType *ot)
1020 {
1021   /* Identifiers */
1022   ot->name = "Copy Keyframes";
1023   ot->idname = "GRAPH_OT_copy";
1024   ot->description = "Copy selected keyframes to the copy/paste buffer";
1025 
1026   /* API callbacks */
1027   ot->exec = graphkeys_copy_exec;
1028   ot->poll = graphop_editable_keyframes_poll;
1029 
1030   /* Flags */
1031   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1032 }
1033 
graphkeys_paste_exec(bContext * C,wmOperator * op)1034 static int graphkeys_paste_exec(bContext *C, wmOperator *op)
1035 {
1036   bAnimContext ac;
1037 
1038   const eKeyPasteOffset offset_mode = RNA_enum_get(op->ptr, "offset");
1039   const eKeyMergeMode merge_mode = RNA_enum_get(op->ptr, "merge");
1040   const bool flipped = RNA_boolean_get(op->ptr, "flipped");
1041 
1042   /* Get editor data. */
1043   if (ANIM_animdata_get_context(C, &ac) == 0) {
1044     return OPERATOR_CANCELLED;
1045   }
1046 
1047   /* Ac.reports by default will be the global reports list, which won't show warnings. */
1048   ac.reports = op->reports;
1049 
1050   /* Paste keyframes - non-zero return means an error occurred while trying to paste. */
1051   if (paste_graph_keys(&ac, offset_mode, merge_mode, flipped)) {
1052     return OPERATOR_CANCELLED;
1053   }
1054 
1055   /* Set notifier that keyframes have changed. */
1056   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
1057 
1058   return OPERATOR_FINISHED;
1059 }
1060 
GRAPH_OT_paste(wmOperatorType * ot)1061 void GRAPH_OT_paste(wmOperatorType *ot)
1062 {
1063   PropertyRNA *prop;
1064 
1065   /* Identifiers */
1066   ot->name = "Paste Keyframes";
1067   ot->idname = "GRAPH_OT_paste";
1068   ot->description =
1069       "Paste keyframes from copy/paste buffer for the selected channels, starting on the current "
1070       "frame";
1071 
1072   /* API callbacks */
1073 
1074   // ot->invoke = WM_operator_props_popup; /* better wait for graph redo panel */
1075   ot->exec = graphkeys_paste_exec;
1076   ot->poll = graphop_editable_keyframes_poll;
1077 
1078   /* Flags */
1079   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1080 
1081   /* Props */
1082   RNA_def_enum(ot->srna,
1083                "offset",
1084                rna_enum_keyframe_paste_offset_items,
1085                KEYFRAME_PASTE_OFFSET_CFRA_START,
1086                "Offset",
1087                "Paste time offset of keys");
1088   RNA_def_enum(ot->srna,
1089                "merge",
1090                rna_enum_keyframe_paste_merge_items,
1091                KEYFRAME_PASTE_MERGE_MIX,
1092                "Type",
1093                "Method of merging pasted keys and existing");
1094   prop = RNA_def_boolean(
1095       ot->srna, "flipped", false, "Flipped", "Paste keyframes from mirrored bones if they exist");
1096   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1097 }
1098 
1099 /* ******************** Duplicate Keyframes Operator ************************* */
1100 
duplicate_graph_keys(bAnimContext * ac)1101 static void duplicate_graph_keys(bAnimContext *ac)
1102 {
1103   ListBase anim_data = {NULL, NULL};
1104   bAnimListElem *ale;
1105   int filter;
1106 
1107   /* Filter data. */
1108   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
1109             ANIMFILTER_NODUPLIS);
1110   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1111 
1112   /* Loop through filtered data and delete selected keys. */
1113   for (ale = anim_data.first; ale; ale = ale->next) {
1114     duplicate_fcurve_keys((FCurve *)ale->key_data);
1115 
1116     ale->update |= ANIM_UPDATE_DEFAULT;
1117   }
1118 
1119   ANIM_animdata_update(ac, &anim_data);
1120   ANIM_animdata_freelist(&anim_data);
1121 }
1122 
1123 /* ------------------- */
1124 
graphkeys_duplicate_exec(bContext * C,wmOperator * UNUSED (op))1125 static int graphkeys_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
1126 {
1127   bAnimContext ac;
1128 
1129   /* Get editor data. */
1130   if (ANIM_animdata_get_context(C, &ac) == 0) {
1131     return OPERATOR_CANCELLED;
1132   }
1133 
1134   /* Duplicate keyframes. */
1135   duplicate_graph_keys(&ac);
1136 
1137   /* Set notifier that keyframes have changed. */
1138   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, NULL);
1139 
1140   return OPERATOR_FINISHED;
1141 }
1142 
GRAPH_OT_duplicate(wmOperatorType * ot)1143 void GRAPH_OT_duplicate(wmOperatorType *ot)
1144 {
1145   /* Identifiers */
1146   ot->name = "Duplicate Keyframes";
1147   ot->idname = "GRAPH_OT_duplicate";
1148   ot->description = "Make a copy of all selected keyframes";
1149 
1150   /* API callbacks */
1151   ot->exec = graphkeys_duplicate_exec;
1152   ot->poll = graphop_editable_keyframes_poll;
1153 
1154   /* Flags */
1155   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1156 
1157   /* To give to transform. */
1158   RNA_def_enum(ot->srna, "mode", rna_enum_transform_mode_types, TFM_TRANSLATION, "Mode", "");
1159 }
1160 
1161 /* ******************** Delete Keyframes Operator ************************* */
1162 
delete_graph_keys(bAnimContext * ac)1163 static bool delete_graph_keys(bAnimContext *ac)
1164 {
1165   ListBase anim_data = {NULL, NULL};
1166   bAnimListElem *ale;
1167   int filter;
1168   bool changed_final = false;
1169 
1170   /* Filter data. */
1171   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
1172             ANIMFILTER_NODUPLIS);
1173   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1174 
1175   /* Loop through filtered data and delete selected keys. */
1176   for (ale = anim_data.first; ale; ale = ale->next) {
1177     FCurve *fcu = (FCurve *)ale->key_data;
1178     AnimData *adt = ale->adt;
1179     bool changed;
1180 
1181     /* Delete selected keyframes only. */
1182     changed = delete_fcurve_keys(fcu);
1183 
1184     if (changed) {
1185       ale->update |= ANIM_UPDATE_DEFAULT;
1186       changed_final = true;
1187     }
1188 
1189     /* Only delete curve too if it won't be doing anything anymore. */
1190     if (BKE_fcurve_is_empty(fcu)) {
1191       ANIM_fcurve_delete_from_animdata(ac, adt, fcu);
1192       ale->key_data = NULL;
1193     }
1194   }
1195 
1196   ANIM_animdata_update(ac, &anim_data);
1197   ANIM_animdata_freelist(&anim_data);
1198 
1199   return changed_final;
1200 }
1201 
1202 /* ------------------- */
1203 
graphkeys_delete_exec(bContext * C,wmOperator * UNUSED (op))1204 static int graphkeys_delete_exec(bContext *C, wmOperator *UNUSED(op))
1205 {
1206   bAnimContext ac;
1207 
1208   /* Get editor data. */
1209   if (ANIM_animdata_get_context(C, &ac) == 0) {
1210     return OPERATOR_CANCELLED;
1211   }
1212 
1213   /* Delete keyframes. */
1214   if (!delete_graph_keys(&ac)) {
1215     return OPERATOR_CANCELLED;
1216   }
1217 
1218   /* Set notifier that keyframes have changed. */
1219   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_REMOVED, NULL);
1220 
1221   return OPERATOR_FINISHED;
1222 }
1223 
GRAPH_OT_delete(wmOperatorType * ot)1224 void GRAPH_OT_delete(wmOperatorType *ot)
1225 {
1226   /* Identifiers */
1227   ot->name = "Delete Keyframes";
1228   ot->idname = "GRAPH_OT_delete";
1229   ot->description = "Remove all selected keyframes";
1230 
1231   /* API callbacks */
1232   ot->invoke = WM_operator_confirm;
1233   ot->exec = graphkeys_delete_exec;
1234   ot->poll = graphop_editable_keyframes_poll;
1235 
1236   /* Flags */
1237   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1238 }
1239 
1240 /* ******************** Clean Keyframes Operator ************************* */
1241 
clean_graph_keys(bAnimContext * ac,float thresh,bool clean_chan)1242 static void clean_graph_keys(bAnimContext *ac, float thresh, bool clean_chan)
1243 {
1244   ListBase anim_data = {NULL, NULL};
1245   bAnimListElem *ale;
1246   int filter;
1247 
1248   /* Filter data. */
1249   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
1250             ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
1251   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1252 
1253   /* Loop through filtered data and clean curves. */
1254   for (ale = anim_data.first; ale; ale = ale->next) {
1255     clean_fcurve(ac, ale, thresh, clean_chan);
1256 
1257     ale->update |= ANIM_UPDATE_DEFAULT;
1258   }
1259 
1260   ANIM_animdata_update(ac, &anim_data);
1261   ANIM_animdata_freelist(&anim_data);
1262 }
1263 
1264 /* ------------------- */
1265 
graphkeys_clean_exec(bContext * C,wmOperator * op)1266 static int graphkeys_clean_exec(bContext *C, wmOperator *op)
1267 {
1268   bAnimContext ac;
1269   float thresh;
1270   bool clean_chan;
1271 
1272   /* Get editor data. */
1273   if (ANIM_animdata_get_context(C, &ac) == 0) {
1274     return OPERATOR_CANCELLED;
1275   }
1276 
1277   /* Get cleaning threshold. */
1278   thresh = RNA_float_get(op->ptr, "threshold");
1279   clean_chan = RNA_boolean_get(op->ptr, "channels");
1280   /* Clean keyframes. */
1281   clean_graph_keys(&ac, thresh, clean_chan);
1282 
1283   /* Set notifier that keyframes have changed. */
1284   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
1285 
1286   return OPERATOR_FINISHED;
1287 }
1288 
GRAPH_OT_clean(wmOperatorType * ot)1289 void GRAPH_OT_clean(wmOperatorType *ot)
1290 {
1291   /* Identifiers */
1292   ot->name = "Clean Keyframes";
1293   ot->idname = "GRAPH_OT_clean";
1294   ot->description = "Simplify F-Curves by removing closely spaced keyframes";
1295 
1296   /* API callbacks */
1297   // ot->invoke = ???; /* XXX we need that number popup for this! */
1298   ot->exec = graphkeys_clean_exec;
1299   ot->poll = graphop_editable_keyframes_poll;
1300 
1301   /* Flags */
1302   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1303 
1304   /* Properties */
1305   ot->prop = RNA_def_float(
1306       ot->srna, "threshold", 0.001f, 0.0f, FLT_MAX, "Threshold", "", 0.0f, 1000.0f);
1307   RNA_def_boolean(ot->srna, "channels", false, "Channels", "");
1308 }
1309 
1310 /* ******************** Decimate Keyframes Operator ************************* */
1311 
decimate_graph_keys(bAnimContext * ac,float remove_ratio,float error_sq_max)1312 static void decimate_graph_keys(bAnimContext *ac, float remove_ratio, float error_sq_max)
1313 {
1314   ListBase anim_data = {NULL, NULL};
1315   bAnimListElem *ale;
1316   int filter;
1317 
1318   /* Filter data. */
1319   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
1320             ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
1321   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1322 
1323   /* Loop through filtered data and clean curves. */
1324   for (ale = anim_data.first; ale; ale = ale->next) {
1325     if (!decimate_fcurve(ale, remove_ratio, error_sq_max)) {
1326       /* The selection contains unsupported keyframe types! */
1327       WM_report(RPT_WARNING, "Decimate: Skipping non linear/bezier keyframes!");
1328     }
1329 
1330     ale->update |= ANIM_UPDATE_DEFAULT;
1331   }
1332 
1333   ANIM_animdata_update(ac, &anim_data);
1334   ANIM_animdata_freelist(&anim_data);
1335 }
1336 
1337 /* ------------------- */
1338 
1339 /* This data type is only used for modal operation. */
1340 typedef struct tDecimateGraphOp {
1341   bAnimContext ac;
1342   Scene *scene;
1343   ScrArea *area;
1344   ARegion *region;
1345 
1346   /** A 0-1 value for determining how much we should decimate. */
1347   PropertyRNA *percentage_prop;
1348 
1349   /** The original bezt curve data (used for restoring fcurves).*/
1350   ListBase bezt_arr_list;
1351 
1352   NumInput num;
1353 } tDecimateGraphOp;
1354 
1355 typedef struct tBeztCopyData {
1356   int tot_vert;
1357   BezTriple *bezt;
1358 } tBeztCopyData;
1359 
1360 typedef enum tDecimModes {
1361   DECIM_RATIO = 1,
1362   DECIM_ERROR,
1363 } tDecimModes;
1364 
1365 /* Overwrite the current bezts arrays with the original data. */
decimate_reset_bezts(tDecimateGraphOp * dgo)1366 static void decimate_reset_bezts(tDecimateGraphOp *dgo)
1367 {
1368   ListBase anim_data = {NULL, NULL};
1369   LinkData *link_bezt;
1370   bAnimListElem *ale;
1371   int filter;
1372 
1373   bAnimContext *ac = &dgo->ac;
1374 
1375   /* Filter data. */
1376   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
1377             ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
1378   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1379 
1380   /* Loop through filtered data and reset bezts. */
1381   for (ale = anim_data.first, link_bezt = dgo->bezt_arr_list.first; ale; ale = ale->next) {
1382     FCurve *fcu = (FCurve *)ale->key_data;
1383 
1384     if (fcu->bezt == NULL) {
1385       /* This curve is baked, skip it. */
1386       continue;
1387     }
1388 
1389     tBeztCopyData *data = link_bezt->data;
1390 
1391     const int arr_size = sizeof(BezTriple) * data->tot_vert;
1392 
1393     MEM_freeN(fcu->bezt);
1394 
1395     fcu->bezt = MEM_mallocN(arr_size, __func__);
1396     fcu->totvert = data->tot_vert;
1397 
1398     memcpy(fcu->bezt, data->bezt, arr_size);
1399 
1400     link_bezt = link_bezt->next;
1401   }
1402 
1403   ANIM_animdata_freelist(&anim_data);
1404 }
1405 
decimate_exit(bContext * C,wmOperator * op)1406 static void decimate_exit(bContext *C, wmOperator *op)
1407 {
1408   tDecimateGraphOp *dgo = op->customdata;
1409   wmWindow *win = CTX_wm_window(C);
1410 
1411   /* If data exists, clear its data and exit. */
1412   if (dgo == NULL) {
1413     return;
1414   }
1415 
1416   ScrArea *area = dgo->area;
1417   LinkData *link;
1418 
1419   for (link = dgo->bezt_arr_list.first; link != NULL; link = link->next) {
1420     tBeztCopyData *copy = link->data;
1421     MEM_freeN(copy->bezt);
1422     MEM_freeN(link->data);
1423   }
1424 
1425   BLI_freelistN(&dgo->bezt_arr_list);
1426   MEM_freeN(dgo);
1427 
1428   /* Return to normal cursor and header status. */
1429   WM_cursor_modal_restore(win);
1430   ED_area_status_text(area, NULL);
1431 
1432   /* Cleanup. */
1433   op->customdata = NULL;
1434 }
1435 
1436 /* Draw a percentage indicator in header. */
decimate_draw_status_header(wmOperator * op,tDecimateGraphOp * dgo)1437 static void decimate_draw_status_header(wmOperator *op, tDecimateGraphOp *dgo)
1438 {
1439   char status_str[UI_MAX_DRAW_STR];
1440   char mode_str[32];
1441 
1442   strcpy(mode_str, TIP_("Decimate Keyframes"));
1443 
1444   if (hasNumInput(&dgo->num)) {
1445     char str_offs[NUM_STR_REP_LEN];
1446 
1447     outputNumInput(&dgo->num, str_offs, &dgo->scene->unit);
1448 
1449     BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, str_offs);
1450   }
1451   else {
1452     float percentage = RNA_property_float_get(op->ptr, dgo->percentage_prop);
1453     BLI_snprintf(
1454         status_str, sizeof(status_str), "%s: %d %%", mode_str, (int)(percentage * 100.0f));
1455   }
1456 
1457   ED_area_status_text(dgo->area, status_str);
1458 }
1459 
1460 /* Calculate percentage based on position of mouse (we only use x-axis for now.
1461  * Since this is more convenient for users to do), and store new percentage value.
1462  */
decimate_mouse_update_percentage(tDecimateGraphOp * dgo,wmOperator * op,const wmEvent * event)1463 static void decimate_mouse_update_percentage(tDecimateGraphOp *dgo,
1464                                              wmOperator *op,
1465                                              const wmEvent *event)
1466 {
1467   float percentage = (event->x - dgo->region->winrct.xmin) / ((float)dgo->region->winx);
1468   RNA_property_float_set(op->ptr, dgo->percentage_prop, percentage);
1469 }
1470 
graphkeys_decimate_invoke(bContext * C,wmOperator * op,const wmEvent * event)1471 static int graphkeys_decimate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1472 {
1473   tDecimateGraphOp *dgo;
1474 
1475   WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EW_SCROLL);
1476 
1477   /* Init slide-op data. */
1478   dgo = op->customdata = MEM_callocN(sizeof(tDecimateGraphOp), "tDecimateGraphOp");
1479 
1480   /* Get editor data. */
1481   if (ANIM_animdata_get_context(C, &dgo->ac) == 0) {
1482     decimate_exit(C, op);
1483     return OPERATOR_CANCELLED;
1484   }
1485 
1486   dgo->percentage_prop = RNA_struct_find_property(op->ptr, "remove_ratio");
1487 
1488   dgo->scene = CTX_data_scene(C);
1489   dgo->area = CTX_wm_area(C);
1490   dgo->region = CTX_wm_region(C);
1491 
1492   /* Initialize percentage so that it will have the correct value before the first mouse move. */
1493   decimate_mouse_update_percentage(dgo, op, event);
1494 
1495   decimate_draw_status_header(op, dgo);
1496 
1497   /* Construct a list with the original bezt arrays so we can restore them during modal operation.
1498    */
1499   {
1500     ListBase anim_data = {NULL, NULL};
1501     bAnimContext *ac = &dgo->ac;
1502     bAnimListElem *ale;
1503 
1504     int filter;
1505 
1506     /* Filter data. */
1507     filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
1508               ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
1509     ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1510 
1511     /* Loop through filtered data and copy the curves. */
1512     for (ale = anim_data.first; ale; ale = ale->next) {
1513       FCurve *fcu = (FCurve *)ale->key_data;
1514 
1515       if (fcu->bezt == NULL) {
1516         /* This curve is baked, skip it. */
1517         continue;
1518       }
1519 
1520       const int arr_size = sizeof(BezTriple) * fcu->totvert;
1521 
1522       tBeztCopyData *copy = MEM_mallocN(sizeof(tBeztCopyData), "bezts_copy");
1523       BezTriple *bezts_copy = MEM_mallocN(arr_size, "bezts_copy_array");
1524 
1525       copy->tot_vert = fcu->totvert;
1526       memcpy(bezts_copy, fcu->bezt, arr_size);
1527 
1528       copy->bezt = bezts_copy;
1529 
1530       LinkData *link = NULL;
1531 
1532       link = MEM_callocN(sizeof(LinkData), "Bezt Link");
1533       link->data = copy;
1534 
1535       BLI_addtail(&dgo->bezt_arr_list, link);
1536     }
1537 
1538     ANIM_animdata_freelist(&anim_data);
1539   }
1540 
1541   if (dgo->bezt_arr_list.first == NULL) {
1542     WM_report(RPT_WARNING,
1543               "Fcurve Decimate: Can't decimate baked channels. Unbake them and try again.");
1544     decimate_exit(C, op);
1545     return OPERATOR_CANCELLED;
1546   }
1547 
1548   WM_event_add_modal_handler(C, op);
1549   return OPERATOR_RUNNING_MODAL;
1550 }
1551 
graphkeys_decimate_modal_update(bContext * C,wmOperator * op)1552 static void graphkeys_decimate_modal_update(bContext *C, wmOperator *op)
1553 {
1554   /* Perform decimate updates - in response to some user action
1555    * (e.g. pressing a key or moving the mouse). */
1556   tDecimateGraphOp *dgo = op->customdata;
1557 
1558   decimate_draw_status_header(op, dgo);
1559 
1560   /* Reset keyframe data (so we get back to the original state). */
1561   decimate_reset_bezts(dgo);
1562 
1563   /* Apply... */
1564   float remove_ratio = RNA_property_float_get(op->ptr, dgo->percentage_prop);
1565   /* We don't want to limit the decimation to a certain error margin. */
1566   const float error_sq_max = FLT_MAX;
1567   decimate_graph_keys(&dgo->ac, remove_ratio, error_sq_max);
1568   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
1569 }
1570 
graphkeys_decimate_modal(bContext * C,wmOperator * op,const wmEvent * event)1571 static int graphkeys_decimate_modal(bContext *C, wmOperator *op, const wmEvent *event)
1572 {
1573   /* This assumes that we are in "DECIM_RATIO" mode. This is because the error margin is very hard
1574    * and finicky to control with this modal mouse grab method. Therefore, it is expected that the
1575    * error margin mode is not adjusted by the modal operator but instead tweaked via the redo
1576    * panel.*/
1577   tDecimateGraphOp *dgo = op->customdata;
1578 
1579   const bool has_numinput = hasNumInput(&dgo->num);
1580 
1581   switch (event->type) {
1582     case LEFTMOUSE: /* Confirm */
1583     case EVT_RETKEY:
1584     case EVT_PADENTER: {
1585       if (event->val == KM_PRESS) {
1586         decimate_exit(C, op);
1587 
1588         return OPERATOR_FINISHED;
1589       }
1590       break;
1591     }
1592 
1593     case EVT_ESCKEY: /* Cancel */
1594     case RIGHTMOUSE: {
1595       if (event->val == KM_PRESS) {
1596         decimate_reset_bezts(dgo);
1597 
1598         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
1599 
1600         decimate_exit(C, op);
1601 
1602         return OPERATOR_CANCELLED;
1603       }
1604       break;
1605     }
1606 
1607     /* Percentage Change... */
1608     case MOUSEMOVE: /* Calculate new position. */
1609     {
1610       if (has_numinput == false) {
1611         /* Update percentage based on position of mouse. */
1612         decimate_mouse_update_percentage(dgo, op, event);
1613 
1614         /* Update pose to reflect the new values. */
1615         graphkeys_decimate_modal_update(C, op);
1616       }
1617       break;
1618     }
1619     default: {
1620       if ((event->val == KM_PRESS) && handleNumInput(C, &dgo->num, event)) {
1621         float value;
1622         float percentage = RNA_property_float_get(op->ptr, dgo->percentage_prop);
1623 
1624         /* Grab percentage from numeric input, and store this new value for redo
1625          * NOTE: users see ints, while internally we use a 0-1 float.
1626          */
1627         value = percentage * 100.0f;
1628         applyNumInput(&dgo->num, &value);
1629 
1630         percentage = value / 100.0f;
1631         RNA_property_float_set(op->ptr, dgo->percentage_prop, percentage);
1632 
1633         /* Update decimate output to reflect the new values. */
1634         graphkeys_decimate_modal_update(C, op);
1635         break;
1636       }
1637 
1638       /* Unhandled event - maybe it was some view manip? */
1639       /* Allow to pass through. */
1640       return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH;
1641     }
1642   }
1643 
1644   return OPERATOR_RUNNING_MODAL;
1645 }
1646 
graphkeys_decimate_exec(bContext * C,wmOperator * op)1647 static int graphkeys_decimate_exec(bContext *C, wmOperator *op)
1648 {
1649   bAnimContext ac;
1650 
1651   /* Get editor data. */
1652   if (ANIM_animdata_get_context(C, &ac) == 0) {
1653     return OPERATOR_CANCELLED;
1654   }
1655 
1656   tDecimModes mode = RNA_enum_get(op->ptr, "mode");
1657   /* We want to be able to work on all available keyframes. */
1658   float remove_ratio = 1.0f;
1659   /* We don't want to limit the decimation to a certain error margin. */
1660   float error_sq_max = FLT_MAX;
1661 
1662   switch (mode) {
1663     case DECIM_RATIO:
1664       remove_ratio = RNA_float_get(op->ptr, "remove_ratio");
1665       break;
1666     case DECIM_ERROR:
1667       error_sq_max = RNA_float_get(op->ptr, "remove_error_margin");
1668       /* The decimate algorithm expects the error to be squared. */
1669       error_sq_max *= error_sq_max;
1670 
1671       break;
1672   }
1673 
1674   if (remove_ratio == 0.0f || error_sq_max == 0.0f) {
1675     /* Nothing to remove. */
1676     return OPERATOR_FINISHED;
1677   }
1678 
1679   decimate_graph_keys(&ac, remove_ratio, error_sq_max);
1680 
1681   /* Set notifier that keyframes have changed. */
1682   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
1683 
1684   return OPERATOR_FINISHED;
1685 }
1686 
graphkeys_decimate_poll_property(const bContext * UNUSED (C),wmOperator * op,const PropertyRNA * prop)1687 static bool graphkeys_decimate_poll_property(const bContext *UNUSED(C),
1688                                              wmOperator *op,
1689                                              const PropertyRNA *prop)
1690 {
1691   const char *prop_id = RNA_property_identifier(prop);
1692 
1693   if (STRPREFIX(prop_id, "remove")) {
1694     int mode = RNA_enum_get(op->ptr, "mode");
1695 
1696     if (STREQ(prop_id, "remove_ratio") && mode != DECIM_RATIO) {
1697       return false;
1698     }
1699     if (STREQ(prop_id, "remove_error_margin") && mode != DECIM_ERROR) {
1700       return false;
1701     }
1702   }
1703 
1704   return true;
1705 }
1706 
graphkeys_decimate_desc(bContext * UNUSED (C),wmOperatorType * UNUSED (op),PointerRNA * ptr)1707 static char *graphkeys_decimate_desc(bContext *UNUSED(C),
1708                                      wmOperatorType *UNUSED(op),
1709                                      PointerRNA *ptr)
1710 {
1711 
1712   if (RNA_enum_get(ptr, "mode") == DECIM_ERROR) {
1713     return BLI_strdup(
1714         "Decimate F-Curves by specifying how much it can deviate from the original curve");
1715   }
1716 
1717   /* Use default description. */
1718   return NULL;
1719 }
1720 
1721 static const EnumPropertyItem decimate_mode_items[] = {
1722     {DECIM_RATIO,
1723      "RATIO",
1724      0,
1725      "Ratio",
1726      "Use a percentage to specify how many keyframes you want to remove"},
1727     {DECIM_ERROR,
1728      "ERROR",
1729      0,
1730      "Error Margin",
1731      "Use an error margin to specify how much the curve is allowed to deviate from the original "
1732      "path"},
1733     {0, NULL, 0, NULL, NULL},
1734 };
1735 
GRAPH_OT_decimate(wmOperatorType * ot)1736 void GRAPH_OT_decimate(wmOperatorType *ot)
1737 {
1738   /* Identifiers */
1739   ot->name = "Decimate Keyframes";
1740   ot->idname = "GRAPH_OT_decimate";
1741   ot->description =
1742       "Decimate F-Curves by removing keyframes that influence the curve shape the least";
1743 
1744   /* API callbacks */
1745   ot->poll_property = graphkeys_decimate_poll_property;
1746   ot->get_description = graphkeys_decimate_desc;
1747   ot->invoke = graphkeys_decimate_invoke;
1748   ot->modal = graphkeys_decimate_modal;
1749   ot->exec = graphkeys_decimate_exec;
1750   ot->poll = graphop_editable_keyframes_poll;
1751 
1752   /* Flags */
1753   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1754 
1755   /* Properties */
1756   RNA_def_enum(ot->srna,
1757                "mode",
1758                decimate_mode_items,
1759                DECIM_RATIO,
1760                "Mode",
1761                "Which mode to use for decimation");
1762 
1763   RNA_def_float_percentage(ot->srna,
1764                            "remove_ratio",
1765                            1.0f / 3.0f,
1766                            0.0f,
1767                            1.0f,
1768                            "Remove",
1769                            "The percentage of keyframes to remove",
1770                            0.0f,
1771                            1.0f);
1772   RNA_def_float(ot->srna,
1773                 "remove_error_margin",
1774                 0.0f,
1775                 0.0f,
1776                 FLT_MAX,
1777                 "Max Error Margin",
1778                 "How much the new decimated curve is allowed to deviate from the original",
1779                 0.0f,
1780                 10.0f);
1781 }
1782 
1783 /* ******************** Bake F-Curve Operator *********************** */
1784 /* This operator bakes the data of the selected F-Curves to F-Points */
1785 
1786 /* Bake each F-Curve into a set of samples. */
bake_graph_curves(bAnimContext * ac,int start,int end)1787 static void bake_graph_curves(bAnimContext *ac, int start, int end)
1788 {
1789   ListBase anim_data = {NULL, NULL};
1790   bAnimListElem *ale;
1791   int filter;
1792 
1793   /* Filter data. */
1794   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL |
1795             ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1796   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1797 
1798   /* Loop through filtered data and add keys between selected keyframes on every frame. */
1799   for (ale = anim_data.first; ale; ale = ale->next) {
1800     FCurve *fcu = (FCurve *)ale->key_data;
1801     ChannelDriver *driver = fcu->driver;
1802 
1803     /* Disable driver so that it don't muck up the sampling process. */
1804     fcu->driver = NULL;
1805 
1806     /* Create samples. */
1807     fcurve_store_samples(fcu, NULL, start, end, fcurve_samplingcb_evalcurve);
1808 
1809     /* Restore driver. */
1810     fcu->driver = driver;
1811 
1812     ale->update |= ANIM_UPDATE_DEPS;
1813   }
1814 
1815   ANIM_animdata_update(ac, &anim_data);
1816   ANIM_animdata_freelist(&anim_data);
1817 }
1818 
1819 /* ------------------- */
1820 
graphkeys_bake_exec(bContext * C,wmOperator * UNUSED (op))1821 static int graphkeys_bake_exec(bContext *C, wmOperator *UNUSED(op))
1822 {
1823   bAnimContext ac;
1824   Scene *scene = NULL;
1825   int start, end;
1826 
1827   /* Get editor data. */
1828   if (ANIM_animdata_get_context(C, &ac) == 0) {
1829     return OPERATOR_CANCELLED;
1830   }
1831 
1832   /* For now, init start/end from preview-range extents. */
1833   /* TODO: add properties for this. (Joshua Leung 2009) */
1834   scene = ac.scene;
1835   start = PSFRA;
1836   end = PEFRA;
1837 
1838   /* Bake keyframes. */
1839   bake_graph_curves(&ac, start, end);
1840 
1841   /* Set notifier that keyframes have changed. */
1842   /* NOTE: some distinction between order/number of keyframes and type should be made? */
1843   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
1844 
1845   return OPERATOR_FINISHED;
1846 }
1847 
GRAPH_OT_bake(wmOperatorType * ot)1848 void GRAPH_OT_bake(wmOperatorType *ot)
1849 {
1850   /* Identifiers */
1851   ot->name = "Bake Curve";
1852   ot->idname = "GRAPH_OT_bake";
1853   ot->description = "Bake selected F-Curves to a set of sampled points defining a similar curve";
1854 
1855   /* API callbacks */
1856   ot->invoke = WM_operator_confirm; /* FIXME */
1857   ot->exec = graphkeys_bake_exec;
1858   ot->poll = graphop_selected_fcurve_poll;
1859 
1860   /* Flags */
1861   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1862 
1863   /* TODO: add props for start/end frames (Joshua Leung 2009) */
1864 }
1865 
1866 #ifdef WITH_AUDASPACE
1867 
1868 /* ******************** Sound Bake F-Curve Operator *********************** */
1869 /* This operator bakes the given sound to the selected F-Curves */
1870 
1871 /* ------------------- */
1872 
1873 /* Custom data storage passed to the F-Sample-ing function,
1874  * which provides the necessary info for baking the sound.
1875  */
1876 typedef struct tSoundBakeInfo {
1877   float *samples;
1878   int length;
1879   int cfra;
1880 } tSoundBakeInfo;
1881 
1882 /* ------------------- */
1883 
1884 /* Sampling callback used to determine the value from the sound to
1885  * save in the F-Curve at the specified frame.
1886  */
fcurve_samplingcb_sound(FCurve * UNUSED (fcu),void * data,float evaltime)1887 static float fcurve_samplingcb_sound(FCurve *UNUSED(fcu), void *data, float evaltime)
1888 {
1889   tSoundBakeInfo *sbi = (tSoundBakeInfo *)data;
1890 
1891   int position = evaltime - sbi->cfra;
1892   if ((position < 0) || (position >= sbi->length)) {
1893     return 0.0f;
1894   }
1895 
1896   return sbi->samples[position];
1897 }
1898 
1899 /* ------------------- */
1900 
graphkeys_sound_bake_exec(bContext * C,wmOperator * op)1901 static int graphkeys_sound_bake_exec(bContext *C, wmOperator *op)
1902 {
1903   bAnimContext ac;
1904   ListBase anim_data = {NULL, NULL};
1905   bAnimListElem *ale;
1906   int filter;
1907 
1908   tSoundBakeInfo sbi;
1909   Scene *scene = NULL;
1910   int start, end;
1911 
1912   char path[FILE_MAX];
1913 
1914   /* Get editor data. */
1915   if (ANIM_animdata_get_context(C, &ac) == 0) {
1916     return OPERATOR_CANCELLED;
1917   }
1918 
1919   RNA_string_get(op->ptr, "filepath", path);
1920 
1921   if (!BLI_is_file(path)) {
1922     BKE_reportf(op->reports, RPT_ERROR, "File not found '%s'", path);
1923     return OPERATOR_CANCELLED;
1924   }
1925 
1926   scene = ac.scene; /* Current scene. */
1927 
1928   /* Store necessary data for the baking steps. */
1929   sbi.samples = AUD_readSoundBuffer(path,
1930                                     RNA_float_get(op->ptr, "low"),
1931                                     RNA_float_get(op->ptr, "high"),
1932                                     RNA_float_get(op->ptr, "attack"),
1933                                     RNA_float_get(op->ptr, "release"),
1934                                     RNA_float_get(op->ptr, "threshold"),
1935                                     RNA_boolean_get(op->ptr, "use_accumulate"),
1936                                     RNA_boolean_get(op->ptr, "use_additive"),
1937                                     RNA_boolean_get(op->ptr, "use_square"),
1938                                     RNA_float_get(op->ptr, "sthreshold"),
1939                                     FPS,
1940                                     &sbi.length);
1941 
1942   if (sbi.samples == NULL) {
1943     BKE_report(op->reports, RPT_ERROR, "Unsupported audio format");
1944     return OPERATOR_CANCELLED;
1945   }
1946 
1947   /* Determine extents of the baking. */
1948   sbi.cfra = start = CFRA;
1949   end = CFRA + sbi.length - 1;
1950 
1951   /* Filter anim channels. */
1952   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL |
1953             ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1954   ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1955 
1956   /* Loop through all selected F-Curves, replacing its data with the sound samples. */
1957   for (ale = anim_data.first; ale; ale = ale->next) {
1958     FCurve *fcu = (FCurve *)ale->key_data;
1959 
1960     /* Sample the sound. */
1961     fcurve_store_samples(fcu, &sbi, start, end, fcurve_samplingcb_sound);
1962 
1963     ale->update |= ANIM_UPDATE_DEFAULT;
1964   }
1965 
1966   /* Free sample data. */
1967   free(sbi.samples);
1968 
1969   /* Validate keyframes after editing. */
1970   ANIM_animdata_update(&ac, &anim_data);
1971   ANIM_animdata_freelist(&anim_data);
1972 
1973   /* Set notifier that 'keyframes' have changed. */
1974   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
1975 
1976   return OPERATOR_FINISHED;
1977 }
1978 
1979 #else /* WITH_AUDASPACE */
1980 
graphkeys_sound_bake_exec(bContext * UNUSED (C),wmOperator * op)1981 static int graphkeys_sound_bake_exec(bContext *UNUSED(C), wmOperator *op)
1982 {
1983   BKE_report(op->reports, RPT_ERROR, "Compiled without sound support");
1984 
1985   return OPERATOR_CANCELLED;
1986 }
1987 
1988 #endif /* WITH_AUDASPACE */
1989 
graphkeys_sound_bake_invoke(bContext * C,wmOperator * op,const wmEvent * event)1990 static int graphkeys_sound_bake_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1991 {
1992   bAnimContext ac;
1993 
1994   /* Verify editor data. */
1995   if (ANIM_animdata_get_context(C, &ac) == 0) {
1996     return OPERATOR_CANCELLED;
1997   }
1998 
1999   return WM_operator_filesel(C, op, event);
2000 }
2001 
GRAPH_OT_sound_bake(wmOperatorType * ot)2002 void GRAPH_OT_sound_bake(wmOperatorType *ot)
2003 {
2004   /* Identifiers */
2005   ot->name = "Bake Sound to F-Curves";
2006   ot->idname = "GRAPH_OT_sound_bake";
2007   ot->description = "Bakes a sound wave to selected F-Curves";
2008 
2009   /* API callbacks */
2010   ot->invoke = graphkeys_sound_bake_invoke;
2011   ot->exec = graphkeys_sound_bake_exec;
2012   ot->poll = graphop_selected_fcurve_poll;
2013 
2014   /* Flags */
2015   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2016 
2017   /* Properties */
2018   WM_operator_properties_filesel(ot,
2019                                  FILE_TYPE_FOLDER | FILE_TYPE_SOUND | FILE_TYPE_MOVIE,
2020                                  FILE_SPECIAL,
2021                                  FILE_OPENFILE,
2022                                  WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS,
2023                                  FILE_DEFAULTDISPLAY,
2024                                  FILE_SORT_ALPHA);
2025   RNA_def_float(ot->srna,
2026                 "low",
2027                 0.0f,
2028                 0.0,
2029                 100000.0,
2030                 "Lowest frequency",
2031                 "Cutoff frequency of a high-pass filter that is applied to the audio data",
2032                 0.1,
2033                 1000.00);
2034   RNA_def_float(ot->srna,
2035                 "high",
2036                 100000.0,
2037                 0.0,
2038                 100000.0,
2039                 "Highest frequency",
2040                 "Cutoff frequency of a low-pass filter that is applied to the audio data",
2041                 0.1,
2042                 1000.00);
2043   RNA_def_float(ot->srna,
2044                 "attack",
2045                 0.005,
2046                 0.0,
2047                 2.0,
2048                 "Attack time",
2049                 "Value for the hull curve calculation that tells how fast the hull curve can rise "
2050                 "(the lower the value the steeper it can rise)",
2051                 0.01,
2052                 0.1);
2053   RNA_def_float(ot->srna,
2054                 "release",
2055                 0.2,
2056                 0.0,
2057                 5.0,
2058                 "Release time",
2059                 "Value for the hull curve calculation that tells how fast the hull curve can fall "
2060                 "(the lower the value the steeper it can fall)",
2061                 0.01,
2062                 0.2);
2063   RNA_def_float(ot->srna,
2064                 "threshold",
2065                 0.0,
2066                 0.0,
2067                 1.0,
2068                 "Threshold",
2069                 "Minimum amplitude value needed to influence the hull curve",
2070                 0.01,
2071                 0.1);
2072   RNA_def_boolean(ot->srna,
2073                   "use_accumulate",
2074                   0,
2075                   "Accumulate",
2076                   "Only the positive differences of the hull curve amplitudes are summarized to "
2077                   "produce the output");
2078   RNA_def_boolean(
2079       ot->srna,
2080       "use_additive",
2081       0,
2082       "Additive",
2083       "The amplitudes of the hull curve are summarized (or, when Accumulate is enabled, "
2084       "both positive and negative differences are accumulated)");
2085   RNA_def_boolean(ot->srna,
2086                   "use_square",
2087                   0,
2088                   "Square",
2089                   "The output is a square curve (negative values always result in -1, and "
2090                   "positive ones in 1)");
2091   RNA_def_float(ot->srna,
2092                 "sthreshold",
2093                 0.1,
2094                 0.0,
2095                 1.0,
2096                 "Square Threshold",
2097                 "Square only: all values with an absolute amplitude lower than that result in 0",
2098                 0.01,
2099                 0.1);
2100 }
2101 
2102 /* ******************** Sample Keyframes Operator *********************** */
2103 /* This operator 'bakes' the values of the curve into new keyframes between pairs
2104  * of selected keyframes. It is useful for creating keyframes for tweaking overlap.
2105  */
2106 
2107 /* Evaluates the curves between each selected keyframe on each frame, and keys the value. */
sample_graph_keys(bAnimContext * ac)2108 static void sample_graph_keys(bAnimContext *ac)
2109 {
2110   ListBase anim_data = {NULL, NULL};
2111   bAnimListElem *ale;
2112   int filter;
2113 
2114   /* filter data */
2115   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
2116             ANIMFILTER_NODUPLIS);
2117   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2118 
2119   /* Loop through filtered data and add keys between selected keyframes on every frame. */
2120   for (ale = anim_data.first; ale; ale = ale->next) {
2121     sample_fcurve((FCurve *)ale->key_data);
2122 
2123     ale->update |= ANIM_UPDATE_DEPS;
2124   }
2125 
2126   ANIM_animdata_update(ac, &anim_data);
2127   ANIM_animdata_freelist(&anim_data);
2128 }
2129 
2130 /* ------------------- */
2131 
graphkeys_sample_exec(bContext * C,wmOperator * UNUSED (op))2132 static int graphkeys_sample_exec(bContext *C, wmOperator *UNUSED(op))
2133 {
2134   bAnimContext ac;
2135 
2136   /* Get editor data. */
2137   if (ANIM_animdata_get_context(C, &ac) == 0) {
2138     return OPERATOR_CANCELLED;
2139   }
2140 
2141   /* Sample keyframes. */
2142   sample_graph_keys(&ac);
2143 
2144   /* Set notifier that keyframes have changed. */
2145   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
2146 
2147   return OPERATOR_FINISHED;
2148 }
2149 
GRAPH_OT_sample(wmOperatorType * ot)2150 void GRAPH_OT_sample(wmOperatorType *ot)
2151 {
2152   /* Identifiers */
2153   ot->name = "Sample Keyframes";
2154   ot->idname = "GRAPH_OT_sample";
2155   ot->description = "Add keyframes on every frame between the selected keyframes";
2156 
2157   /* API callbacks */
2158   ot->exec = graphkeys_sample_exec;
2159   ot->poll = graphop_editable_keyframes_poll;
2160 
2161   /* Flags */
2162   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2163 }
2164 
2165 /* ************************************************************************** */
2166 /* SETTINGS STUFF */
2167 
2168 /* ******************** Set Extrapolation-Type Operator *********************** */
2169 
2170 /* Defines for make/clear cyclic extrapolation tools. */
2171 #define MAKE_CYCLIC_EXPO -1
2172 #define CLEAR_CYCLIC_EXPO -2
2173 
2174 /* Defines for set extrapolation-type for selected keyframes tool. */
2175 static const EnumPropertyItem prop_graphkeys_expo_types[] = {
2176     {FCURVE_EXTRAPOLATE_CONSTANT,
2177      "CONSTANT",
2178      0,
2179      "Constant Extrapolation",
2180      "Values on endpoint keyframes are held"},
2181     {FCURVE_EXTRAPOLATE_LINEAR,
2182      "LINEAR",
2183      0,
2184      "Linear Extrapolation",
2185      "Straight-line slope of end segments are extended past the endpoint keyframes"},
2186 
2187     {MAKE_CYCLIC_EXPO,
2188      "MAKE_CYCLIC",
2189      0,
2190      "Make Cyclic (F-Modifier)",
2191      "Add Cycles F-Modifier if one doesn't exist already"},
2192     {CLEAR_CYCLIC_EXPO,
2193      "CLEAR_CYCLIC",
2194      0,
2195      "Clear Cyclic (F-Modifier)",
2196      "Remove Cycles F-Modifier if not needed anymore"},
2197     {0, NULL, 0, NULL, NULL},
2198 };
2199 
2200 /* This function is responsible for setting extrapolation mode for keyframes. */
setexpo_graph_keys(bAnimContext * ac,short mode)2201 static void setexpo_graph_keys(bAnimContext *ac, short mode)
2202 {
2203   ListBase anim_data = {NULL, NULL};
2204   bAnimListElem *ale;
2205   int filter;
2206 
2207   /* Filter data. */
2208   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL |
2209             ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
2210   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2211 
2212   /* Loop through setting mode per F-Curve. */
2213   for (ale = anim_data.first; ale; ale = ale->next) {
2214     FCurve *fcu = (FCurve *)ale->data;
2215 
2216     if (mode >= 0) {
2217       /* Just set mode setting. */
2218       fcu->extend = mode;
2219 
2220       ale->update |= ANIM_UPDATE_HANDLES;
2221     }
2222     else {
2223       /* Shortcuts for managing Cycles F-Modifiers to make it easier to toggle cyclic animation
2224        * without having to go through FModifier UI in Graph Editor to do so.
2225        */
2226       if (mode == MAKE_CYCLIC_EXPO) {
2227         /* Only add if one doesn't exist. */
2228         if (list_has_suitable_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES, -1) == 0) {
2229           /* TODO: add some more preset versions which set different extrapolation options?
2230            * (Joshua Leung 2011) */
2231           add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES, fcu);
2232         }
2233       }
2234       else if (mode == CLEAR_CYCLIC_EXPO) {
2235         /* Remove all the modifiers fitting this description. */
2236         FModifier *fcm, *fcn = NULL;
2237 
2238         for (fcm = fcu->modifiers.first; fcm; fcm = fcn) {
2239           fcn = fcm->next;
2240 
2241           if (fcm->type == FMODIFIER_TYPE_CYCLES) {
2242             remove_fmodifier(&fcu->modifiers, fcm);
2243           }
2244         }
2245       }
2246     }
2247 
2248     ale->update |= ANIM_UPDATE_DEPS;
2249   }
2250 
2251   ANIM_animdata_update(ac, &anim_data);
2252   ANIM_animdata_freelist(&anim_data);
2253 }
2254 
2255 /* ------------------- */
2256 
graphkeys_expo_exec(bContext * C,wmOperator * op)2257 static int graphkeys_expo_exec(bContext *C, wmOperator *op)
2258 {
2259   bAnimContext ac;
2260   short mode;
2261 
2262   /* Get editor data. */
2263   if (ANIM_animdata_get_context(C, &ac) == 0) {
2264     return OPERATOR_CANCELLED;
2265   }
2266 
2267   /* Get handle setting mode. */
2268   mode = RNA_enum_get(op->ptr, "type");
2269 
2270   /* Set handle type. */
2271   setexpo_graph_keys(&ac, mode);
2272 
2273   /* Set notifier that keyframe properties have changed. */
2274   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL);
2275 
2276   return OPERATOR_FINISHED;
2277 }
2278 
GRAPH_OT_extrapolation_type(wmOperatorType * ot)2279 void GRAPH_OT_extrapolation_type(wmOperatorType *ot)
2280 {
2281   /* Identifiers */
2282   ot->name = "Set Keyframe Extrapolation";
2283   ot->idname = "GRAPH_OT_extrapolation_type";
2284   ot->description = "Set extrapolation mode for selected F-Curves";
2285 
2286   /* API callbacks */
2287   ot->invoke = WM_menu_invoke;
2288   ot->exec = graphkeys_expo_exec;
2289   ot->poll = graphop_editable_keyframes_poll;
2290 
2291   /* Flags */
2292   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2293 
2294   /* Id-props */
2295   ot->prop = RNA_def_enum(ot->srna, "type", prop_graphkeys_expo_types, 0, "Type", "");
2296 }
2297 
2298 /* ******************** Set Interpolation-Type Operator *********************** */
2299 
2300 /* This function is responsible for setting interpolation mode for keyframes. */
setipo_graph_keys(bAnimContext * ac,short mode)2301 static void setipo_graph_keys(bAnimContext *ac, short mode)
2302 {
2303   ListBase anim_data = {NULL, NULL};
2304   bAnimListElem *ale;
2305   int filter;
2306   KeyframeEditFunc set_cb = ANIM_editkeyframes_ipo(mode);
2307 
2308   /* Filter data. */
2309   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
2310             ANIMFILTER_NODUPLIS);
2311   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2312 
2313   /* Loop through setting BezTriple interpolation
2314    * Note: we do not supply KeyframeEditData to the looper yet.
2315    * Currently that's not necessary here.
2316    */
2317   for (ale = anim_data.first; ale; ale = ale->next) {
2318     ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, calchandles_fcurve);
2319 
2320     ale->update |= ANIM_UPDATE_DEFAULT_NOHANDLES;
2321   }
2322 
2323   ANIM_animdata_update(ac, &anim_data);
2324   ANIM_animdata_freelist(&anim_data);
2325 }
2326 
2327 /* ------------------- */
2328 
graphkeys_ipo_exec(bContext * C,wmOperator * op)2329 static int graphkeys_ipo_exec(bContext *C, wmOperator *op)
2330 {
2331   bAnimContext ac;
2332   short mode;
2333 
2334   /* Get editor data. */
2335   if (ANIM_animdata_get_context(C, &ac) == 0) {
2336     return OPERATOR_CANCELLED;
2337   }
2338 
2339   /* Get handle setting mode. */
2340   mode = RNA_enum_get(op->ptr, "type");
2341 
2342   /* Set handle type. */
2343   setipo_graph_keys(&ac, mode);
2344 
2345   /* Set notifier that keyframe properties have changed. */
2346   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL);
2347 
2348   return OPERATOR_FINISHED;
2349 }
2350 
GRAPH_OT_interpolation_type(wmOperatorType * ot)2351 void GRAPH_OT_interpolation_type(wmOperatorType *ot)
2352 {
2353   /* Identifiers */
2354   ot->name = "Set Keyframe Interpolation";
2355   ot->idname = "GRAPH_OT_interpolation_type";
2356   ot->description =
2357       "Set interpolation mode for the F-Curve segments starting from the selected keyframes";
2358 
2359   /* API callbacks */
2360   ot->invoke = WM_menu_invoke;
2361   ot->exec = graphkeys_ipo_exec;
2362   ot->poll = graphop_editable_keyframes_poll;
2363 
2364   /* Flags */
2365   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2366 
2367   /* Id-props */
2368   ot->prop = RNA_def_enum(
2369       ot->srna, "type", rna_enum_beztriple_interpolation_mode_items, 0, "Type", "");
2370 }
2371 
2372 /* ******************** Set Easing Operator *********************** */
2373 
seteasing_graph_keys(bAnimContext * ac,short mode)2374 static void seteasing_graph_keys(bAnimContext *ac, short mode)
2375 {
2376   ListBase anim_data = {NULL, NULL};
2377   bAnimListElem *ale;
2378   int filter;
2379   KeyframeEditFunc set_cb = ANIM_editkeyframes_easing(mode);
2380 
2381   /* Filter data. */
2382   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
2383             ANIMFILTER_NODUPLIS);
2384   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2385 
2386   /* Loop through setting BezTriple easing.
2387    * Note: we do not supply KeyframeEditData to the looper yet.
2388    * Currently that's not necessary here.
2389    */
2390   for (ale = anim_data.first; ale; ale = ale->next) {
2391     ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, calchandles_fcurve);
2392 
2393     ale->update |= ANIM_UPDATE_DEFAULT_NOHANDLES;
2394   }
2395 
2396   ANIM_animdata_update(ac, &anim_data);
2397   ANIM_animdata_freelist(&anim_data);
2398 }
2399 
graphkeys_easing_exec(bContext * C,wmOperator * op)2400 static int graphkeys_easing_exec(bContext *C, wmOperator *op)
2401 {
2402   bAnimContext ac;
2403   short mode;
2404 
2405   /* Get editor data. */
2406   if (ANIM_animdata_get_context(C, &ac) == 0) {
2407     return OPERATOR_CANCELLED;
2408   }
2409 
2410   /* Get handle setting mode. */
2411   mode = RNA_enum_get(op->ptr, "type");
2412 
2413   /* Set handle type. */
2414   seteasing_graph_keys(&ac, mode);
2415 
2416   /* Set notifier that keyframe properties have changed. */
2417   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL);
2418 
2419   return OPERATOR_FINISHED;
2420 }
2421 
GRAPH_OT_easing_type(wmOperatorType * ot)2422 void GRAPH_OT_easing_type(wmOperatorType *ot)
2423 {
2424   /* Identifiers */
2425   ot->name = "Set Keyframe Easing Type";
2426   ot->idname = "GRAPH_OT_easing_type";
2427   ot->description =
2428       "Set easing type for the F-Curve segments starting from the selected keyframes";
2429 
2430   /* API callbacks */
2431   ot->invoke = WM_menu_invoke;
2432   ot->exec = graphkeys_easing_exec;
2433   ot->poll = graphop_editable_keyframes_poll;
2434 
2435   /* Flags */
2436   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2437 
2438   /* Id-props */
2439   ot->prop = RNA_def_enum(
2440       ot->srna, "type", rna_enum_beztriple_interpolation_easing_items, 0, "Type", "");
2441 }
2442 
2443 /* ******************** Set Handle-Type Operator *********************** */
2444 
2445 /* This function is responsible for setting handle-type of selected keyframes. */
sethandles_graph_keys(bAnimContext * ac,short mode)2446 static void sethandles_graph_keys(bAnimContext *ac, short mode)
2447 {
2448   ListBase anim_data = {NULL, NULL};
2449   bAnimListElem *ale;
2450   int filter;
2451 
2452   KeyframeEditFunc edit_cb = ANIM_editkeyframes_handles(mode);
2453   KeyframeEditFunc sel_cb = ANIM_editkeyframes_ok(BEZT_OK_SELECTED);
2454 
2455   /* Filter data. */
2456   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
2457             ANIMFILTER_NODUPLIS);
2458   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2459 
2460   /* Loop through setting flags for handles.
2461    * Note: we do not supply KeyframeEditData to the looper yet.
2462    * Currently that's not necessary here.
2463    */
2464   for (ale = anim_data.first; ale; ale = ale->next) {
2465     FCurve *fcu = (FCurve *)ale->key_data;
2466 
2467     /* Any selected keyframes for editing? */
2468     if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, sel_cb, NULL)) {
2469       /* Change type of selected handles. */
2470       ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, calchandles_fcurve);
2471 
2472       ale->update |= ANIM_UPDATE_DEFAULT;
2473     }
2474   }
2475 
2476   ANIM_animdata_update(ac, &anim_data);
2477   ANIM_animdata_freelist(&anim_data);
2478 }
2479 /* ------------------- */
2480 
graphkeys_handletype_exec(bContext * C,wmOperator * op)2481 static int graphkeys_handletype_exec(bContext *C, wmOperator *op)
2482 {
2483   bAnimContext ac;
2484   short mode;
2485 
2486   /* Get editor data. */
2487   if (ANIM_animdata_get_context(C, &ac) == 0) {
2488     return OPERATOR_CANCELLED;
2489   }
2490 
2491   /* Get handle setting mode. */
2492   mode = RNA_enum_get(op->ptr, "type");
2493 
2494   /* Set handle type. */
2495   sethandles_graph_keys(&ac, mode);
2496 
2497   /* Set notifier that keyframe properties have changed. */
2498   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL);
2499 
2500   return OPERATOR_FINISHED;
2501 }
2502 
GRAPH_OT_handle_type(wmOperatorType * ot)2503 void GRAPH_OT_handle_type(wmOperatorType *ot)
2504 {
2505   /* Identifiers */
2506   ot->name = "Set Keyframe Handle Type";
2507   ot->idname = "GRAPH_OT_handle_type";
2508   ot->description = "Set type of handle for selected keyframes";
2509 
2510   /* API callbacks */
2511   ot->invoke = WM_menu_invoke;
2512   ot->exec = graphkeys_handletype_exec;
2513   ot->poll = graphop_editable_keyframes_poll;
2514 
2515   /* Flags */
2516   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2517 
2518   /* Id-props */
2519   ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_keyframe_handle_type_items, 0, "Type", "");
2520 }
2521 
2522 /* ************************************************************************** */
2523 /* TRANSFORM STUFF */
2524 
2525 /* ***************** 'Euler Filter' Operator **************************** */
2526 /* Euler filter tools (as seen in Maya), are necessary for working with 'baked'
2527  * rotation curves (with Euler rotations). The main purpose of such tools is to
2528  * resolve any discontinuities that may arise in the curves due to the clamping
2529  * of values to -180 degrees to 180 degrees.
2530  */
2531 
2532 /* Set of three euler-rotation F-Curves. */
2533 typedef struct tEulerFilter {
2534   struct tEulerFilter *next, *prev;
2535 
2536   /** ID-block which owns the channels */
2537   ID *id;
2538   /** 3 Pointers to F-Curves. */
2539   FCurve *(fcurves[3]);
2540   /** Pointer to one of the RNA Path's used by one of the F-Curves. */
2541   const char *rna_path;
2542 } tEulerFilter;
2543 
graphkeys_euler_filter_exec(bContext * C,wmOperator * op)2544 static int graphkeys_euler_filter_exec(bContext *C, wmOperator *op)
2545 {
2546   bAnimContext ac;
2547 
2548   ListBase anim_data = {NULL, NULL};
2549   bAnimListElem *ale;
2550   int filter;
2551 
2552   ListBase eulers = {NULL, NULL};
2553   tEulerFilter *euf = NULL;
2554   int groups = 0, failed = 0;
2555 
2556   /* Get editor data. */
2557   if (ANIM_animdata_get_context(C, &ac) == 0) {
2558     return OPERATOR_CANCELLED;
2559   }
2560 
2561   /* The process is done in two passes:
2562    * 1) Sets of three related rotation curves are identified from the selected channels,
2563    *    and are stored as a single 'operation unit' for the next step.
2564    * 2) Each set of three F-Curves is processed for each keyframe, with the values being
2565    *    processed as necessary.
2566    */
2567 
2568   /* Step 1: extract only the rotation f-curves. */
2569   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_CURVE_VISIBLE |
2570             ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
2571   ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
2572 
2573   for (ale = anim_data.first; ale; ale = ale->next) {
2574     FCurve *fcu = (FCurve *)ale->data;
2575 
2576     /* Check if this is an appropriate F-Curve
2577      * - Only rotation curves.
2578      * - For pchan curves, make sure we're only using the euler curves.
2579      */
2580     if (strstr(fcu->rna_path, "rotation_euler") == NULL) {
2581       continue;
2582     }
2583     if (ELEM(fcu->array_index, 0, 1, 2) == 0) {
2584       BKE_reportf(op->reports,
2585                   RPT_WARNING,
2586                   "Euler Rotation F-Curve has invalid index (ID='%s', Path='%s', Index=%d)",
2587                   (ale->id) ? ale->id->name : TIP_("<No ID>"),
2588                   fcu->rna_path,
2589                   fcu->array_index);
2590       continue;
2591     }
2592 
2593     /* Optimization: assume that xyz curves will always be stored consecutively,
2594      * so if the paths or the ID's don't match up, then a curve needs to be added
2595      * to a new group.
2596      */
2597     if ((euf) && (euf->id == ale->id) && (STREQ(euf->rna_path, fcu->rna_path))) {
2598       /* This should be fine to add to the existing group then. */
2599       euf->fcurves[fcu->array_index] = fcu;
2600     }
2601     else {
2602       /* Just add to a new block. */
2603       euf = MEM_callocN(sizeof(tEulerFilter), "tEulerFilter");
2604       BLI_addtail(&eulers, euf);
2605       groups++;
2606 
2607       euf->id = ale->id;
2608       /* This should be safe, since we're only using it for a short time. */
2609       euf->rna_path = fcu->rna_path;
2610       euf->fcurves[fcu->array_index] = fcu;
2611     }
2612 
2613     ale->update |= ANIM_UPDATE_DEFAULT;
2614   }
2615 
2616   if (groups == 0) {
2617     ANIM_animdata_freelist(&anim_data);
2618     BKE_report(op->reports, RPT_WARNING, "No Euler Rotation F-Curves to fix up");
2619     return OPERATOR_CANCELLED;
2620   }
2621 
2622   /* Step 2: go through each set of curves, processing the values at each keyframe.
2623    * - It is assumed that there must be a full set of keyframes at each keyframe position.
2624    */
2625   for (euf = eulers.first; euf; euf = euf->next) {
2626     int f;
2627 
2628     /* Sanity check: ensure that there are enough F-Curves to work on in this group. */
2629     /* TODO: also enforce assumption that there be a full set of keyframes
2630      * at each position by ensuring that totvert counts are same? (Joshua Leung 2011) */
2631     if (ELEM(NULL, euf->fcurves[0], euf->fcurves[1], euf->fcurves[2])) {
2632       /* Report which components are missing. */
2633       BKE_reportf(op->reports,
2634                   RPT_WARNING,
2635                   "Missing %s%s%s component(s) of euler rotation for ID='%s' and RNA-Path='%s'",
2636                   (euf->fcurves[0] == NULL) ? "X" : "",
2637                   (euf->fcurves[1] == NULL) ? "Y" : "",
2638                   (euf->fcurves[2] == NULL) ? "Z" : "",
2639                   euf->id->name,
2640                   euf->rna_path);
2641 
2642       /* Keep track of number of failed sets, and carry on to next group. */
2643       failed++;
2644       continue;
2645     }
2646 
2647     /* Simple method: just treat any difference between
2648      * keys of greater than 180 degrees as being a flip. */
2649     /* FIXME: there are more complicated methods that
2650      * will be needed to fix more cases than just some */
2651     for (f = 0; f < 3; f++) {
2652       FCurve *fcu = euf->fcurves[f];
2653       BezTriple *bezt, *prev;
2654       uint i;
2655 
2656       /* Skip if not enough vets to do a decent analysis of.... */
2657       if (fcu->totvert <= 2) {
2658         continue;
2659       }
2660 
2661       /* Prev follows bezt, bezt = "current" point to be fixed. */
2662       /* Our method depends on determining a "difference" from the previous vert. */
2663       for (i = 1, prev = fcu->bezt, bezt = fcu->bezt + 1; i < fcu->totvert; i++, prev = bezt++) {
2664         const float sign = (prev->vec[1][1] > bezt->vec[1][1]) ? 1.0f : -1.0f;
2665 
2666         /* > 180 degree flip? */
2667         if ((sign * (prev->vec[1][1] - bezt->vec[1][1])) >= (float)M_PI) {
2668           /* 360 degrees to add/subtract frame value until difference
2669            * is acceptably small that there's no more flip. */
2670           const float fac = sign * 2.0f * (float)M_PI;
2671 
2672           while ((sign * (prev->vec[1][1] - bezt->vec[1][1])) >= (float)M_PI) {
2673             bezt->vec[0][1] += fac;
2674             bezt->vec[1][1] += fac;
2675             bezt->vec[2][1] += fac;
2676           }
2677         }
2678       }
2679     }
2680   }
2681   BLI_freelistN(&eulers);
2682 
2683   ANIM_animdata_update(&ac, &anim_data);
2684   ANIM_animdata_freelist(&anim_data);
2685 
2686   /* Updates + finishing warnings. */
2687   if (failed == groups) {
2688     BKE_report(
2689         op->reports,
2690         RPT_ERROR,
2691         "No Euler Rotations could be corrected, ensure each rotation has keys for all components, "
2692         "and that F-Curves for these are in consecutive XYZ order and selected");
2693     return OPERATOR_CANCELLED;
2694   }
2695 
2696   if (failed) {
2697     BKE_report(
2698         op->reports,
2699         RPT_ERROR,
2700         "Some Euler Rotations could not be corrected due to missing/unselected/out-of-order "
2701         "F-Curves, "
2702         "ensure each rotation has keys for all components, and that F-Curves for these are in "
2703         "consecutive XYZ order and selected");
2704   }
2705 
2706   /* Set notifier that keyframes have changed. */
2707   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
2708 
2709   /* Done at last. */
2710   return OPERATOR_FINISHED;
2711 }
2712 
GRAPH_OT_euler_filter(wmOperatorType * ot)2713 void GRAPH_OT_euler_filter(wmOperatorType *ot)
2714 {
2715   /* Identifiers */
2716   ot->name = "Euler Discontinuity Filter";
2717   ot->idname = "GRAPH_OT_euler_filter";
2718   ot->description =
2719       "Fix large jumps and flips in the selected "
2720       "Euler Rotation F-Curves arising from rotation "
2721       "values being clipped when baking physics";
2722 
2723   /* API callbacks */
2724   ot->exec = graphkeys_euler_filter_exec;
2725   ot->poll = graphop_editable_keyframes_poll;
2726 
2727   /* Flags */
2728   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2729 }
2730 
2731 /* ***************** Jump to Selected Frames Operator *********************** */
2732 
graphkeys_framejump_poll(bContext * C)2733 static bool graphkeys_framejump_poll(bContext *C)
2734 {
2735   /* Prevent changes during render. */
2736   if (G.is_rendering) {
2737     return false;
2738   }
2739 
2740   return graphop_visible_keyframes_poll(C);
2741 }
2742 
sum_selected_keyframes(bAnimContext * ac)2743 static KeyframeEditData sum_selected_keyframes(bAnimContext *ac)
2744 {
2745   ListBase anim_data = {NULL, NULL};
2746   bAnimListElem *ale;
2747   int filter;
2748   KeyframeEditData ked;
2749 
2750   /* Init edit data. */
2751   memset(&ked, 0, sizeof(KeyframeEditData));
2752 
2753   /* Loop over action data, averaging values. */
2754   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
2755   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2756 
2757   for (ale = anim_data.first; ale; ale = ale->next) {
2758     AnimData *adt = ANIM_nla_mapping_get(ac, ale);
2759     short mapping_flag = ANIM_get_normalization_flags(ac);
2760     KeyframeEditData current_ked;
2761     float offset;
2762     float unit_scale = ANIM_unit_mapping_get_factor(
2763         ac->scene, ale->id, ale->key_data, mapping_flag | ANIM_UNITCONV_ONLYKEYS, &offset);
2764 
2765     memset(&current_ked, 0, sizeof(current_ked));
2766 
2767     if (adt) {
2768       ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
2769       ANIM_fcurve_keyframes_loop(&current_ked, ale->key_data, NULL, bezt_calc_average, NULL);
2770       ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
2771     }
2772     else {
2773       ANIM_fcurve_keyframes_loop(&current_ked, ale->key_data, NULL, bezt_calc_average, NULL);
2774     }
2775 
2776     ked.f1 += current_ked.f1;
2777     ked.i1 += current_ked.i1;
2778     ked.f2 += (current_ked.f2 + offset) * unit_scale;
2779     ked.i2 += current_ked.i2;
2780   }
2781 
2782   ANIM_animdata_freelist(&anim_data);
2783 
2784   return ked;
2785 }
2786 
2787 /* Snap current-frame indicator to 'average time' of selected keyframe. */
graphkeys_framejump_exec(bContext * C,wmOperator * UNUSED (op))2788 static int graphkeys_framejump_exec(bContext *C, wmOperator *UNUSED(op))
2789 {
2790   bAnimContext ac;
2791 
2792   /* Get editor data. */
2793   if (ANIM_animdata_get_context(C, &ac) == 0) {
2794     return OPERATOR_CANCELLED;
2795   }
2796 
2797   const KeyframeEditData keyframe_sum = sum_selected_keyframes(&ac);
2798   const float sum_time = keyframe_sum.f1;
2799   const float sum_value = keyframe_sum.f2;
2800   const int num_keyframes = keyframe_sum.i1;
2801 
2802   if (num_keyframes == 0) {
2803     return OPERATOR_FINISHED;
2804   }
2805 
2806   /* Set the new current frame and cursor values, based on the average time and value. */
2807   SpaceGraph *sipo = (SpaceGraph *)ac.sl;
2808   Scene *scene = ac.scene;
2809 
2810   /* Take the average values, rounding to the nearest int as necessary for int results. */
2811   if (sipo->mode == SIPO_MODE_DRIVERS) {
2812     /* Drivers Mode - Affects cursor (float) */
2813     sipo->cursorTime = sum_time / (float)num_keyframes;
2814   }
2815   else {
2816     /* Animation Mode - Affects current frame (int) */
2817     CFRA = round_fl_to_int(sum_time / num_keyframes);
2818     SUBFRA = 0.f;
2819   }
2820   sipo->cursorVal = sum_value / (float)num_keyframes;
2821 
2822   /* Set notifier that things have changed. */
2823   WM_event_add_notifier(C, NC_SCENE | ND_FRAME, ac.scene);
2824 
2825   return OPERATOR_FINISHED;
2826 }
2827 
GRAPH_OT_frame_jump(wmOperatorType * ot)2828 void GRAPH_OT_frame_jump(wmOperatorType *ot)
2829 {
2830   /* Identifiers */
2831   ot->name = "Jump to Keyframes";
2832   ot->idname = "GRAPH_OT_frame_jump";
2833   ot->description = "Place the cursor on the midpoint of selected keyframes";
2834 
2835   /* API callbacks */
2836   ot->exec = graphkeys_framejump_exec;
2837   ot->poll = graphkeys_framejump_poll;
2838 
2839   /* Flags */
2840   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2841 }
2842 
2843 /* snap 2D cursor value to the average value of selected keyframe */
graphkeys_snap_cursor_value_exec(bContext * C,wmOperator * UNUSED (op))2844 static int graphkeys_snap_cursor_value_exec(bContext *C, wmOperator *UNUSED(op))
2845 {
2846   bAnimContext ac;
2847 
2848   if (ANIM_animdata_get_context(C, &ac) == 0) {
2849     return OPERATOR_CANCELLED;
2850   }
2851 
2852   const KeyframeEditData keyframe_sum = sum_selected_keyframes(&ac);
2853   const float sum_value = keyframe_sum.f2;
2854   const int num_keyframes = keyframe_sum.i1;
2855 
2856   if (num_keyframes == 0) {
2857     return OPERATOR_FINISHED;
2858   }
2859 
2860   SpaceGraph *sipo = (SpaceGraph *)ac.sl;
2861   sipo->cursorVal = sum_value / (float)num_keyframes;
2862   // WM_event_add_notifier(C, NC_SCENE | ND_FRAME, ac.scene);
2863   ED_region_tag_redraw(CTX_wm_region(C));
2864 
2865   return OPERATOR_FINISHED;
2866 }
2867 
GRAPH_OT_snap_cursor_value(wmOperatorType * ot)2868 void GRAPH_OT_snap_cursor_value(wmOperatorType *ot)
2869 {
2870   /* Identifiers. */
2871   ot->name = "Snap Cursor Value to Selected";
2872   ot->idname = "GRAPH_OT_snap_cursor_value";
2873   ot->description = "Place the cursor value on the average value of selected keyframes";
2874 
2875   /* API callbacks. */
2876   ot->exec = graphkeys_snap_cursor_value_exec;
2877   ot->poll = graphkeys_framejump_poll;
2878 
2879   /* Flags */
2880   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2881 }
2882 
2883 /* ******************** Snap Keyframes Operator *********************** */
2884 
2885 /* Defines for snap keyframes tool. */
2886 static const EnumPropertyItem prop_graphkeys_snap_types[] = {
2887     {GRAPHKEYS_SNAP_CFRA,
2888      "CFRA",
2889      0,
2890      "Selection to Current Frame",
2891      "Snap selected keyframes to the current frame"},
2892     {GRAPHKEYS_SNAP_VALUE,
2893      "VALUE",
2894      0,
2895      "Selection to Cursor Value",
2896      "Set values of selected keyframes to the cursor value (Y/Horizontal component)"},
2897     {GRAPHKEYS_SNAP_NEAREST_FRAME,
2898      "NEAREST_FRAME",
2899      0,
2900      "Selection to Nearest Frame",
2901      "Snap selected keyframes to the nearest (whole) frame (use to fix accidental sub-frame "
2902      "offsets)"},
2903     {GRAPHKEYS_SNAP_NEAREST_SECOND,
2904      "NEAREST_SECOND",
2905      0,
2906      "Selection to Nearest Second",
2907      "Snap selected keyframes to the nearest second"},
2908     {GRAPHKEYS_SNAP_NEAREST_MARKER,
2909      "NEAREST_MARKER",
2910      0,
2911      "Selection to Nearest Marker",
2912      "Snap selected keyframes to the nearest marker"},
2913     {GRAPHKEYS_SNAP_HORIZONTAL,
2914      "HORIZONTAL",
2915      0,
2916      "Flatten Handles",
2917      "Flatten handles for a smoother transition"},
2918     {0, NULL, 0, NULL, NULL},
2919 };
2920 
2921 /* This function is responsible for snapping keyframes to frame-times. */
snap_graph_keys(bAnimContext * ac,short mode)2922 static void snap_graph_keys(bAnimContext *ac, short mode)
2923 {
2924   ListBase anim_data = {NULL, NULL};
2925   bAnimListElem *ale;
2926   int filter;
2927 
2928   SpaceGraph *sipo = (SpaceGraph *)ac->sl;
2929   KeyframeEditData ked;
2930   KeyframeEditFunc edit_cb;
2931   float cursor_value = 0.0f;
2932 
2933   /* Filter data. */
2934   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
2935             ANIMFILTER_NODUPLIS);
2936   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2937 
2938   /* Init custom data for iterating over keyframes. */
2939   memset(&ked, 0, sizeof(KeyframeEditData));
2940   ked.scene = ac->scene;
2941   if (mode == GRAPHKEYS_SNAP_NEAREST_MARKER) {
2942     ked.list.first = (ac->markers) ? ac->markers->first : NULL;
2943     ked.list.last = (ac->markers) ? ac->markers->last : NULL;
2944   }
2945   else if (mode == GRAPHKEYS_SNAP_VALUE) {
2946     cursor_value = (sipo) ? sipo->cursorVal : 0.0f;
2947   }
2948   else if (mode == GRAPHKEYS_SNAP_CFRA) {
2949     /* In drivers mode, use the cursor value instead
2950      * (We need to use a different callback for that though)
2951      */
2952     if (sipo->mode == SIPO_MODE_DRIVERS) {
2953       ked.f1 = sipo->cursorTime;
2954       mode = SNAP_KEYS_TIME;
2955     }
2956   }
2957 
2958   /* Get beztriple editing callbacks. */
2959   edit_cb = ANIM_editkeyframes_snap(mode);
2960 
2961   /* Snap keyframes. */
2962   for (ale = anim_data.first; ale; ale = ale->next) {
2963     AnimData *adt = ANIM_nla_mapping_get(ac, ale);
2964 
2965     /* Normalise cursor value (for normalised F-Curves display). */
2966     if (mode == GRAPHKEYS_SNAP_VALUE) {
2967       short mapping_flag = ANIM_get_normalization_flags(ac);
2968       float offset;
2969       float unit_scale = ANIM_unit_mapping_get_factor(
2970           ac->scene, ale->id, ale->key_data, mapping_flag, &offset);
2971 
2972       ked.f1 = (cursor_value / unit_scale) - offset;
2973     }
2974 
2975     /* Perform snapping. */
2976     if (adt) {
2977       ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0);
2978       ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
2979       ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0);
2980     }
2981     else {
2982       ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
2983     }
2984 
2985     ale->update |= ANIM_UPDATE_DEFAULT;
2986   }
2987 
2988   ANIM_animdata_update(ac, &anim_data);
2989   ANIM_animdata_freelist(&anim_data);
2990 }
2991 
2992 /* ------------------- */
2993 
graphkeys_snap_exec(bContext * C,wmOperator * op)2994 static int graphkeys_snap_exec(bContext *C, wmOperator *op)
2995 {
2996   bAnimContext ac;
2997   short mode;
2998 
2999   /* Get editor data. */
3000   if (ANIM_animdata_get_context(C, &ac) == 0) {
3001     return OPERATOR_CANCELLED;
3002   }
3003 
3004   /* Get snapping mode. */
3005   mode = RNA_enum_get(op->ptr, "type");
3006 
3007   /* Snap keyframes. */
3008   snap_graph_keys(&ac, mode);
3009 
3010   /* Set notifier that keyframes have changed. */
3011   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
3012 
3013   return OPERATOR_FINISHED;
3014 }
3015 
GRAPH_OT_snap(wmOperatorType * ot)3016 void GRAPH_OT_snap(wmOperatorType *ot)
3017 {
3018   /* Identifiers */
3019   ot->name = "Snap Keys";
3020   ot->idname = "GRAPH_OT_snap";
3021   ot->description = "Snap selected keyframes to the chosen times/values";
3022 
3023   /* API callbacks */
3024   ot->invoke = WM_menu_invoke;
3025   ot->exec = graphkeys_snap_exec;
3026   ot->poll = graphop_editable_keyframes_poll;
3027 
3028   /* Flags */
3029   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3030 
3031   /* Id-props */
3032   ot->prop = RNA_def_enum(ot->srna, "type", prop_graphkeys_snap_types, 0, "Type", "");
3033 }
3034 
3035 /* ******************** Mirror Keyframes Operator *********************** */
3036 
3037 /* Defines for mirror keyframes tool. */
3038 static const EnumPropertyItem prop_graphkeys_mirror_types[] = {
3039     {GRAPHKEYS_MIRROR_CFRA,
3040      "CFRA",
3041      0,
3042      "By Times Over Current Frame",
3043      "Flip times of selected keyframes using the current frame as the mirror line"},
3044     {GRAPHKEYS_MIRROR_VALUE,
3045      "VALUE",
3046      0,
3047      "By Values Over Cursor Value",
3048      "Flip values of selected keyframes using the cursor value (Y/Horizontal component) as the "
3049      "mirror line"},
3050     {GRAPHKEYS_MIRROR_YAXIS,
3051      "YAXIS",
3052      0,
3053      "By Times Over Time=0",
3054      "Flip times of selected keyframes, effectively reversing the order they appear in"},
3055     {GRAPHKEYS_MIRROR_XAXIS,
3056      "XAXIS",
3057      0,
3058      "By Values Over Value=0",
3059      "Flip values of selected keyframes (i.e. negative values become positive, and vice versa)"},
3060     {GRAPHKEYS_MIRROR_MARKER,
3061      "MARKER",
3062      0,
3063      "By Times Over First Selected Marker",
3064      "Flip times of selected keyframes using the first selected marker as the reference point"},
3065     {0, NULL, 0, NULL, NULL},
3066 };
3067 
3068 /* This function is responsible for mirroring keyframes. */
mirror_graph_keys(bAnimContext * ac,short mode)3069 static void mirror_graph_keys(bAnimContext *ac, short mode)
3070 {
3071   ListBase anim_data = {NULL, NULL};
3072   bAnimListElem *ale;
3073   int filter;
3074 
3075   SpaceGraph *sipo = (SpaceGraph *)ac->sl;
3076   KeyframeEditData ked;
3077   KeyframeEditFunc edit_cb;
3078   float cursor_value = 0.0f;
3079 
3080   /* Init custom data for looping over keyframes. */
3081   memset(&ked, 0, sizeof(KeyframeEditData));
3082   ked.scene = ac->scene;
3083 
3084   /* Store mode-specific custom data... */
3085   if (mode == GRAPHKEYS_MIRROR_MARKER) {
3086     TimeMarker *marker = NULL;
3087 
3088     /* Find first selected marker. */
3089     marker = ED_markers_get_first_selected(ac->markers);
3090 
3091     /* Store marker's time (if available). */
3092     if (marker) {
3093       ked.f1 = (float)marker->frame;
3094     }
3095     else {
3096       return;
3097     }
3098   }
3099   else if (mode == GRAPHKEYS_MIRROR_VALUE) {
3100     cursor_value = (sipo) ? sipo->cursorVal : 0.0f;
3101   }
3102   else if (mode == GRAPHKEYS_MIRROR_CFRA) {
3103     /* In drivers mode, use the cursor value instead
3104      * (We need to use a different callback for that though)
3105      */
3106     if (sipo->mode == SIPO_MODE_DRIVERS) {
3107       ked.f1 = sipo->cursorTime;
3108       mode = MIRROR_KEYS_TIME;
3109     }
3110   }
3111 
3112   /* Get beztriple editing callbacks. */
3113   edit_cb = ANIM_editkeyframes_mirror(mode);
3114 
3115   /* Filter data. */
3116   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
3117             ANIMFILTER_NODUPLIS);
3118   ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
3119 
3120   /* Mirror keyframes. */
3121   for (ale = anim_data.first; ale; ale = ale->next) {
3122     AnimData *adt = ANIM_nla_mapping_get(ac, ale);
3123 
3124     /* Apply unit corrections. */
3125     if (mode == GRAPHKEYS_MIRROR_VALUE) {
3126       short mapping_flag = ANIM_get_normalization_flags(ac);
3127       float offset;
3128       float unit_scale = ANIM_unit_mapping_get_factor(
3129           ac->scene, ale->id, ale->key_data, mapping_flag | ANIM_UNITCONV_ONLYKEYS, &offset);
3130 
3131       ked.f1 = (cursor_value + offset) * unit_scale;
3132     }
3133 
3134     /* Perform actual mirroring. */
3135     if (adt) {
3136       ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0);
3137       ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
3138       ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0);
3139     }
3140     else {
3141       ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
3142     }
3143 
3144     ale->update |= ANIM_UPDATE_DEFAULT;
3145   }
3146 
3147   ANIM_animdata_update(ac, &anim_data);
3148   ANIM_animdata_freelist(&anim_data);
3149 }
3150 
3151 /* ------------------- */
3152 
graphkeys_mirror_exec(bContext * C,wmOperator * op)3153 static int graphkeys_mirror_exec(bContext *C, wmOperator *op)
3154 {
3155   bAnimContext ac;
3156   short mode;
3157 
3158   /* Get editor data. */
3159   if (ANIM_animdata_get_context(C, &ac) == 0) {
3160     return OPERATOR_CANCELLED;
3161   }
3162 
3163   /* Get mirroring mode. */
3164   mode = RNA_enum_get(op->ptr, "type");
3165 
3166   /* Mirror keyframes. */
3167   mirror_graph_keys(&ac, mode);
3168 
3169   /* Set notifier that keyframes have changed. */
3170   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
3171 
3172   return OPERATOR_FINISHED;
3173 }
3174 
GRAPH_OT_mirror(wmOperatorType * ot)3175 void GRAPH_OT_mirror(wmOperatorType *ot)
3176 {
3177   /* Identifiers */
3178   ot->name = "Mirror Keys";
3179   ot->idname = "GRAPH_OT_mirror";
3180   ot->description = "Flip selected keyframes over the selected mirror line";
3181 
3182   /* API callbacks */
3183   ot->invoke = WM_menu_invoke;
3184   ot->exec = graphkeys_mirror_exec;
3185   ot->poll = graphop_editable_keyframes_poll;
3186 
3187   /* Flags */
3188   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3189 
3190   /* Id-props */
3191   ot->prop = RNA_def_enum(ot->srna, "type", prop_graphkeys_mirror_types, 0, "Type", "");
3192 }
3193 
3194 /* ******************** Smooth Keyframes Operator *********************** */
3195 
graphkeys_smooth_exec(bContext * C,wmOperator * UNUSED (op))3196 static int graphkeys_smooth_exec(bContext *C, wmOperator *UNUSED(op))
3197 {
3198   bAnimContext ac;
3199   ListBase anim_data = {NULL, NULL};
3200   bAnimListElem *ale;
3201   int filter;
3202 
3203   /* Get editor data. */
3204   if (ANIM_animdata_get_context(C, &ac) == 0) {
3205     return OPERATOR_CANCELLED;
3206   }
3207 
3208   /* Filter data. */
3209   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT |
3210             ANIMFILTER_NODUPLIS);
3211   ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
3212 
3213   /* Smooth keyframes. */
3214   for (ale = anim_data.first; ale; ale = ale->next) {
3215     /* For now, we can only smooth by flattening handles AND smoothing curve values.
3216      * Perhaps the mode argument could be removed, as that functionality is offered through
3217      * Snap->Flatten Handles anyway.
3218      */
3219     smooth_fcurve(ale->key_data);
3220 
3221     ale->update |= ANIM_UPDATE_DEFAULT;
3222   }
3223 
3224   ANIM_animdata_update(&ac, &anim_data);
3225   ANIM_animdata_freelist(&anim_data);
3226 
3227   /* Set notifier that keyframes have changed. */
3228   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
3229 
3230   return OPERATOR_FINISHED;
3231 }
3232 
GRAPH_OT_smooth(wmOperatorType * ot)3233 void GRAPH_OT_smooth(wmOperatorType *ot)
3234 {
3235   /* Identifiers */
3236   ot->name = "Smooth Keys";
3237   ot->idname = "GRAPH_OT_smooth";
3238   ot->description = "Apply weighted moving means to make selected F-Curves less bumpy";
3239 
3240   /* API callbacks */
3241   ot->exec = graphkeys_smooth_exec;
3242   ot->poll = graphop_editable_keyframes_poll;
3243 
3244   /* Flags */
3245   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3246 }
3247 
3248 /* ************************************************************************** */
3249 /* F-CURVE MODIFIERS */
3250 
3251 /* ******************** Add F-Modifier Operator *********************** */
3252 
graph_fmodifier_itemf(bContext * C,PointerRNA * UNUSED (ptr),PropertyRNA * UNUSED (prop),bool * r_free)3253 static const EnumPropertyItem *graph_fmodifier_itemf(bContext *C,
3254                                                      PointerRNA *UNUSED(ptr),
3255                                                      PropertyRNA *UNUSED(prop),
3256                                                      bool *r_free)
3257 {
3258   EnumPropertyItem *item = NULL;
3259   int totitem = 0;
3260   int i = 0;
3261 
3262   if (C == NULL) {
3263     return rna_enum_fmodifier_type_items;
3264   }
3265 
3266   /* Start from 1 to skip the 'Invalid' modifier type. */
3267   for (i = 1; i < FMODIFIER_NUM_TYPES; i++) {
3268     const FModifierTypeInfo *fmi = get_fmodifier_typeinfo(i);
3269     int index;
3270 
3271     /* Check if modifier is valid for this context. */
3272     if (fmi == NULL) {
3273       continue;
3274     }
3275 
3276     index = RNA_enum_from_value(rna_enum_fmodifier_type_items, fmi->type);
3277     if (index != -1) { /* Not all types are implemented yet... */
3278       RNA_enum_item_add(&item, &totitem, &rna_enum_fmodifier_type_items[index]);
3279     }
3280   }
3281 
3282   RNA_enum_item_end(&item, &totitem);
3283   *r_free = true;
3284 
3285   return item;
3286 }
3287 
graph_fmodifier_add_exec(bContext * C,wmOperator * op)3288 static int graph_fmodifier_add_exec(bContext *C, wmOperator *op)
3289 {
3290   bAnimContext ac;
3291   ListBase anim_data = {NULL, NULL};
3292   bAnimListElem *ale;
3293   int filter;
3294   short type;
3295 
3296   /* Get editor data. */
3297   if (ANIM_animdata_get_context(C, &ac) == 0) {
3298     return OPERATOR_CANCELLED;
3299   }
3300 
3301   /* Get type of modifier to add. */
3302   type = RNA_enum_get(op->ptr, "type");
3303 
3304   /* Filter data. */
3305   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
3306   if (RNA_boolean_get(op->ptr, "only_active")) {
3307     /* FIXME: enforce in this case only a single channel to get handled? */
3308     filter |= ANIMFILTER_ACTIVE;
3309   }
3310   else {
3311     filter |= (ANIMFILTER_SEL | ANIMFILTER_CURVE_VISIBLE);
3312   }
3313   ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
3314 
3315   /* Add f-modifier to each curve. */
3316   for (ale = anim_data.first; ale; ale = ale->next) {
3317     FCurve *fcu = (FCurve *)ale->data;
3318     FModifier *fcm;
3319 
3320     /* Add F-Modifier of specified type to active F-Curve, and make it the active one. */
3321     fcm = add_fmodifier(&fcu->modifiers, type, fcu);
3322     if (fcm) {
3323       set_active_fmodifier(&fcu->modifiers, fcm);
3324     }
3325     else {
3326       BKE_report(op->reports, RPT_ERROR, "Modifier could not be added (see console for details)");
3327       break;
3328     }
3329 
3330     ale->update |= ANIM_UPDATE_DEPS;
3331   }
3332 
3333   ANIM_animdata_update(&ac, &anim_data);
3334   ANIM_animdata_freelist(&anim_data);
3335 
3336   /* Set notifier that things have changed. */
3337   WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
3338 
3339   return OPERATOR_FINISHED;
3340 }
3341 
GRAPH_OT_fmodifier_add(wmOperatorType * ot)3342 void GRAPH_OT_fmodifier_add(wmOperatorType *ot)
3343 {
3344   PropertyRNA *prop;
3345 
3346   /* Identifiers */
3347   ot->name = "Add F-Curve Modifier";
3348   ot->idname = "GRAPH_OT_fmodifier_add";
3349   ot->description = "Add F-Modifier to the active/selected F-Curves";
3350 
3351   /* API callbacks */
3352   ot->invoke = WM_menu_invoke;
3353   ot->exec = graph_fmodifier_add_exec;
3354   ot->poll = graphop_selected_fcurve_poll;
3355 
3356   /* Flags */
3357   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3358 
3359   /* Id-props */
3360   prop = RNA_def_enum(ot->srna, "type", rna_enum_fmodifier_type_items, 0, "Type", "");
3361   RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ACTION);
3362   RNA_def_enum_funcs(prop, graph_fmodifier_itemf);
3363   ot->prop = prop;
3364 
3365   RNA_def_boolean(
3366       ot->srna, "only_active", 1, "Only Active", "Only add F-Modifier to active F-Curve");
3367 }
3368 
3369 /* ******************** Copy F-Modifiers Operator *********************** */
3370 
graph_fmodifier_copy_exec(bContext * C,wmOperator * op)3371 static int graph_fmodifier_copy_exec(bContext *C, wmOperator *op)
3372 {
3373   bAnimContext ac;
3374   bAnimListElem *ale;
3375   bool ok = false;
3376 
3377   /* Get editor data. */
3378   if (ANIM_animdata_get_context(C, &ac) == 0) {
3379     return OPERATOR_CANCELLED;
3380   }
3381 
3382   /* Clear buffer first. */
3383   ANIM_fmodifiers_copybuf_free();
3384 
3385   /* Get the active F-Curve. */
3386   ale = get_active_fcurve_channel(&ac);
3387 
3388   /* If this exists, call the copy F-Modifiers API function. */
3389   if (ale && ale->data) {
3390     FCurve *fcu = (FCurve *)ale->data;
3391 
3392     /* TODO: When 'active' vs 'all' boolean is added, change last param! (Joshua Leung 2010) */
3393     ok = ANIM_fmodifiers_copy_to_buf(&fcu->modifiers, 0);
3394 
3395     /* Free temp data now. */
3396     MEM_freeN(ale);
3397   }
3398 
3399   /* Successful or not? */
3400   if (ok == 0) {
3401     BKE_report(op->reports, RPT_ERROR, "No F-Modifiers available to be copied");
3402     return OPERATOR_CANCELLED;
3403   }
3404   return OPERATOR_FINISHED;
3405 }
3406 
GRAPH_OT_fmodifier_copy(wmOperatorType * ot)3407 void GRAPH_OT_fmodifier_copy(wmOperatorType *ot)
3408 {
3409   /* Identifiers */
3410   ot->name = "Copy F-Modifiers";
3411   ot->idname = "GRAPH_OT_fmodifier_copy";
3412   ot->description = "Copy the F-Modifier(s) of the active F-Curve";
3413 
3414   /* API callbacks */
3415   ot->exec = graph_fmodifier_copy_exec;
3416   ot->poll = graphop_active_fcurve_poll;
3417 
3418   /* Flags */
3419   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3420 
3421   /* Id-props */
3422 #if 0
3423   ot->prop = RNA_def_boolean(ot->srna,
3424                              "all",
3425                              1,
3426                              "All F-Modifiers",
3427                              "Copy all the F-Modifiers, instead of just the active one");
3428 #endif
3429 }
3430 
3431 /* ******************** Paste F-Modifiers Operator *********************** */
3432 
graph_fmodifier_paste_exec(bContext * C,wmOperator * op)3433 static int graph_fmodifier_paste_exec(bContext *C, wmOperator *op)
3434 {
3435   bAnimContext ac;
3436 
3437   ListBase anim_data = {NULL, NULL};
3438   bAnimListElem *ale;
3439   int filter;
3440 
3441   const bool replace = RNA_boolean_get(op->ptr, "replace");
3442   bool ok = false;
3443 
3444   /* Get editor data. */
3445   if (ANIM_animdata_get_context(C, &ac) == 0) {
3446     return OPERATOR_CANCELLED;
3447   }
3448 
3449   /* Filter data. */
3450   if (RNA_boolean_get(op->ptr, "only_active")) {
3451     /* This should be the default (for buttons) - Just paste to the active FCurve. */
3452     filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ACTIVE | ANIMFILTER_FOREDIT |
3453               ANIMFILTER_NODUPLIS);
3454   }
3455   else {
3456     /* This is only if the operator gets called from a hotkey or search -
3457      * Paste to all visible curves. */
3458     filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL |
3459               ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
3460   }
3461 
3462   ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
3463 
3464   /* Paste modifiers. */
3465   for (ale = anim_data.first; ale; ale = ale->next) {
3466     FCurve *fcu = (FCurve *)ale->data;
3467     int tot;
3468 
3469     tot = ANIM_fmodifiers_paste_from_buf(&fcu->modifiers, replace, fcu);
3470 
3471     if (tot) {
3472       ale->update |= ANIM_UPDATE_DEPS;
3473       ok = true;
3474     }
3475   }
3476 
3477   if (ok) {
3478     ANIM_animdata_update(&ac, &anim_data);
3479   }
3480   ANIM_animdata_freelist(&anim_data);
3481 
3482   /* Successful or not?. */
3483   if (ok) {
3484     /* Set notifier that keyframes have changed. */
3485     WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
3486 
3487     return OPERATOR_FINISHED;
3488   }
3489 
3490   BKE_report(op->reports, RPT_ERROR, "No F-Modifiers to paste");
3491   return OPERATOR_CANCELLED;
3492 }
3493 
GRAPH_OT_fmodifier_paste(wmOperatorType * ot)3494 void GRAPH_OT_fmodifier_paste(wmOperatorType *ot)
3495 {
3496   /* Identifiers */
3497   ot->name = "Paste F-Modifiers";
3498   ot->idname = "GRAPH_OT_fmodifier_paste";
3499   ot->description = "Add copied F-Modifiers to the selected F-Curves";
3500 
3501   /* API callbacks */
3502   ot->exec = graph_fmodifier_paste_exec;
3503   ot->poll = graphop_active_fcurve_poll;
3504 
3505   /* Flags */
3506   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3507 
3508   /* Properties */
3509   RNA_def_boolean(
3510       ot->srna, "only_active", true, "Only Active", "Only paste F-Modifiers on active F-Curve");
3511   RNA_def_boolean(
3512       ot->srna,
3513       "replace",
3514       false,
3515       "Replace Existing",
3516       "Replace existing F-Modifiers, instead of just appending to the end of the existing list");
3517 }
3518 
3519 /* ************************************************************************** */
3520 /* Drivers */
3521 
3522 /* ******************** Copy Driver Vars Operator *********************** */
3523 
graph_driver_vars_copy_exec(bContext * C,wmOperator * op)3524 static int graph_driver_vars_copy_exec(bContext *C, wmOperator *op)
3525 {
3526   bool ok = false;
3527 
3528   PointerRNA ptr = CTX_data_pointer_get_type(C, "active_editable_fcurve", &RNA_FCurve);
3529 
3530   /* If this exists, call the copy driver vars API function. */
3531   FCurve *fcu = ptr.data;
3532 
3533   if (fcu) {
3534     ok = ANIM_driver_vars_copy(op->reports, fcu);
3535   }
3536 
3537   /* Successful or not?. */
3538   if (ok) {
3539     return OPERATOR_FINISHED;
3540   }
3541   return OPERATOR_CANCELLED;
3542 }
3543 
GRAPH_OT_driver_variables_copy(wmOperatorType * ot)3544 void GRAPH_OT_driver_variables_copy(wmOperatorType *ot)
3545 {
3546   /* Identifiers */
3547   ot->name = "Copy Driver Variables";
3548   ot->idname = "GRAPH_OT_driver_variables_copy";
3549   ot->description = "Copy the driver variables of the active driver";
3550 
3551   /* API callbacks */
3552   ot->exec = graph_driver_vars_copy_exec;
3553   ot->poll = graphop_active_editable_fcurve_ctx_poll;
3554 
3555   /* Flags */
3556   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3557 }
3558 
3559 /* ******************** Paste Driver Vars Operator *********************** */
3560 
graph_driver_vars_paste_exec(bContext * C,wmOperator * op)3561 static int graph_driver_vars_paste_exec(bContext *C, wmOperator *op)
3562 {
3563   const bool replace = RNA_boolean_get(op->ptr, "replace");
3564   bool ok = false;
3565 
3566   PointerRNA ptr = CTX_data_pointer_get_type(C, "active_editable_fcurve", &RNA_FCurve);
3567 
3568   /* If this exists, call the paste driver vars API function. */
3569   FCurve *fcu = ptr.data;
3570 
3571   if (fcu) {
3572     ok = ANIM_driver_vars_paste(op->reports, fcu, replace);
3573   }
3574 
3575   /* Successful or not?. */
3576   if (ok) {
3577     /* Rebuild depsgraph, now that there are extra deps here. */
3578     DEG_relations_tag_update(CTX_data_main(C));
3579 
3580     /* Set notifier that keyframes have changed. */
3581     WM_event_add_notifier(C, NC_SCENE | ND_FRAME, CTX_data_scene(C));
3582 
3583     return OPERATOR_FINISHED;
3584   }
3585   return OPERATOR_CANCELLED;
3586 }
3587 
GRAPH_OT_driver_variables_paste(wmOperatorType * ot)3588 void GRAPH_OT_driver_variables_paste(wmOperatorType *ot)
3589 {
3590   /* Identifiers */
3591   ot->name = "Paste Driver Variables";
3592   ot->idname = "GRAPH_OT_driver_variables_paste";
3593   ot->description = "Add copied driver variables to the active driver";
3594 
3595   /* API callbacks */
3596   ot->exec = graph_driver_vars_paste_exec;
3597   ot->poll = graphop_active_editable_fcurve_ctx_poll;
3598 
3599   /* Flags */
3600   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3601 
3602   /* Properties */
3603   RNA_def_boolean(ot->srna,
3604                   "replace",
3605                   false,
3606                   "Replace Existing",
3607                   "Replace existing driver variables, instead of just appending to the end of the "
3608                   "existing list");
3609 }
3610 
3611 /* ************************************************************************** */
3612 
graph_driver_delete_invalid_exec(bContext * C,wmOperator * op)3613 static int graph_driver_delete_invalid_exec(bContext *C, wmOperator *op)
3614 {
3615   bAnimContext ac;
3616   ListBase anim_data = {NULL, NULL};
3617   bAnimListElem *ale;
3618   int filter;
3619   bool ok = false;
3620   uint deleted = 0;
3621 
3622   /* Get editor data. */
3623   if (ANIM_animdata_get_context(C, &ac) == 0) {
3624     return OPERATOR_CANCELLED;
3625   }
3626 
3627   /* NOTE: We might need a scene update to evaluate the driver flags. */
3628 
3629   /* Filter data. */
3630   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
3631   ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
3632 
3633   /* Find invalid drivers. */
3634   for (ale = anim_data.first; ale; ale = ale->next) {
3635     FCurve *fcu = (FCurve *)ale->data;
3636     if (ELEM(NULL, fcu, fcu->driver)) {
3637       continue;
3638     }
3639     if (!(fcu->driver->flag & DRIVER_FLAG_INVALID)) {
3640       continue;
3641     }
3642 
3643     ok |= ANIM_remove_driver(op->reports, ale->id, fcu->rna_path, fcu->array_index, 0);
3644     if (!ok) {
3645       break;
3646     }
3647     deleted += 1;
3648   }
3649 
3650   /* Cleanup. */
3651   ANIM_animdata_freelist(&anim_data);
3652 
3653   if (deleted > 0) {
3654     /* Notify the world of any changes. */
3655     DEG_relations_tag_update(CTX_data_main(C));
3656     WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_REMOVED, NULL);
3657     WM_reportf(RPT_INFO, "Deleted %u drivers", deleted);
3658   }
3659   else {
3660     WM_report(RPT_INFO, "No drivers deleted");
3661   }
3662 
3663   /* Successful or not?*/
3664   if (!ok) {
3665     return OPERATOR_CANCELLED;
3666   }
3667 
3668   return OPERATOR_FINISHED;
3669 }
3670 
graph_driver_delete_invalid_poll(bContext * C)3671 static bool graph_driver_delete_invalid_poll(bContext *C)
3672 {
3673   bAnimContext ac;
3674   ScrArea *area = CTX_wm_area(C);
3675 
3676   /* Firstly, check if in Graph Editor. */
3677   if ((area == NULL) || (area->spacetype != SPACE_GRAPH)) {
3678     return false;
3679   }
3680 
3681   /* Try to init Anim-Context stuff ourselves and check. */
3682   return ANIM_animdata_get_context(C, &ac) != 0;
3683 }
3684 
GRAPH_OT_driver_delete_invalid(wmOperatorType * ot)3685 void GRAPH_OT_driver_delete_invalid(wmOperatorType *ot)
3686 {
3687   /* Identifiers */
3688   ot->name = "Delete Invalid Drivers";
3689   ot->idname = "GRAPH_OT_driver_delete_invalid";
3690   ot->description = "Delete all visible drivers considered invalid";
3691 
3692   /* API callbacks */
3693   ot->exec = graph_driver_delete_invalid_exec;
3694   ot->poll = graph_driver_delete_invalid_poll;
3695 
3696   /* Flags */
3697   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3698 }
3699