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 = ®ion->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(¤t_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(¤t_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(¤t_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