Blender V5.0
keyframes_edit.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2008 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cfloat>
10#include <cmath>
11#include <cstdlib>
12#include <cstring>
13
14#include "MEM_guardedalloc.h"
15
16#include "BLI_function_ref.hh"
17#include "BLI_lasso_2d.hh"
18#include "BLI_listbase.h"
19#include "BLI_math_vector.h"
20#include "BLI_rect.h"
21#include "BLI_utildefines.h"
22
23#include "DNA_anim_types.h"
24#include "DNA_object_types.h"
25#include "DNA_scene_types.h"
26
27#include "BKE_fcurve.hh"
28#include "BKE_nla.hh"
29
30#include "ED_anim_api.hh"
31#include "ED_keyframes_edit.hh"
32#include "ED_markers.hh"
33
34#include "ANIM_action.hh"
35
36using namespace blender;
37
38/* This file defines an API and set of callback-operators for
39 * non-destructive editing of keyframe data.
40 *
41 * Two API functions are defined for actually performing the operations on the data:
42 * ANIM_fcurve_keyframes_loop()
43 * which take the data they operate on, a few callbacks defining what operations to perform.
44 *
45 * As operators which work on keyframes usually apply the same operation on all BezTriples in
46 * every channel, the code has been optimized providing a set of functions which will get the
47 * appropriate bezier-modify function to set. These functions (ANIM_editkeyframes_*) will need
48 * to be called before getting any channels.
49 *
50 * A set of 'validation' callbacks are provided for checking if a BezTriple should be operated on.
51 * These should only be used when using a 'general' BezTriple editor (i.e. selection setters which
52 * don't check existing selection status).
53 *
54 * - Joshua Leung, Dec 2008
55 */
56
57/* ************************************************************************** */
58/* Keyframe Editing Loops - Exposed API */
59
60/* --------------------------- Base Functions ------------------------------------ */
61
63 FCurve *fcu,
64 KeyframeEditFunc key_ok,
65 KeyframeEditFunc key_cb,
66 FcuEditFunc fcu_cb)
67{
68 BezTriple *bezt;
69 short ok = 0;
70 uint i;
71
72 /* sanity check */
73 if (ELEM(nullptr, fcu, fcu->bezt)) {
74 return 0;
75 }
76
77 /* Set the F-Curve into the edit-data so that it can be accessed. */
78 if (ked) {
79 ked->fcu = fcu;
80 ked->curIndex = 0;
82 }
83
84 /* if function to apply to bezier curves is set, then loop through executing it on beztriples */
85 if (key_cb) {
86 /* if there's a validation func, include that check in the loop
87 * (this is should be more efficient than checking for it in every loop)
88 */
89 if (key_ok) {
90 for (bezt = fcu->bezt, i = 0; i < fcu->totvert; bezt++, i++) {
91 if (ked) {
92 /* advance the index, and reset the ok flags (to not influence the result) */
93 ked->curIndex = i;
95 }
96
97 /* Only operate on this BezTriple if it fulfills the criteria of the validation func */
98 if ((ok = key_ok(ked, bezt))) {
99 if (ked) {
100 ked->curflags = eKeyframeVertOk(ok);
101 }
102
103 /* Exit with return-code '1' if function returns positive
104 * This is useful if finding if some BezTriple satisfies a condition.
105 */
106 if (key_cb(ked, bezt)) {
107 return 1;
108 }
109 }
110 }
111 }
112 else {
113 for (bezt = fcu->bezt, i = 0; i < fcu->totvert; bezt++, i++) {
114 if (ked) {
115 ked->curIndex = i;
116 }
117
118 /* Exit with return-code '1' if function returns positive
119 * This is useful if finding if some BezTriple satisfies a condition.
120 */
121 if (key_cb(ked, bezt)) {
122 return 1;
123 }
124 }
125 }
126 }
127
128 /* Unset the F-Curve from the edit-data now that it's done. */
129 if (ked) {
130 ked->fcu = nullptr;
131 ked->curIndex = 0;
132 ked->curflags = KEYFRAME_NONE;
133 }
134
135 /* if fcu_cb (F-Curve post-editing callback) has been specified then execute it */
136 if (fcu_cb) {
137 fcu_cb(fcu);
138 }
139
140 /* done */
141 return 0;
142}
143
144/* --------------------- Further Abstracted (Not Exposed Directly) ----------------------------- */
145
146/* This function is used to loop over the keyframe data in an Action Group */
148 bActionGroup *agrp,
149 KeyframeEditFunc key_ok,
150 KeyframeEditFunc key_cb,
151 FcuEditFunc fcu_cb)
152{
153 /* sanity check */
154 if (agrp == nullptr) {
155 return 0;
156 }
157
158 /* Legacy actions. */
159 if (agrp->wrap().is_legacy()) {
160 LISTBASE_FOREACH (FCurve *, fcu, &agrp->channels) {
161 if (fcu->grp == agrp) {
162 if (ANIM_fcurve_keyframes_loop(ked, fcu, key_ok, key_cb, fcu_cb)) {
163 return 1;
164 }
165 }
166 }
167 return 0;
168 }
169
170 /* Layered actions. */
171 animrig::Channelbag &channelbag = agrp->channelbag->wrap();
172 Span<FCurve *> fcurves = channelbag.fcurves().slice(agrp->fcurve_range_start,
173 agrp->fcurve_range_length);
174 for (FCurve *fcurve : fcurves) {
175 if (ANIM_fcurve_keyframes_loop(ked, fcurve, key_ok, key_cb, fcu_cb)) {
176 return 1;
177 }
178 }
179
180 return 0;
181}
182
183/* Loop over all keyframes in the layered Action. */
185 animrig::Action &action,
186 animrig::Slot *slot,
187 KeyframeEditFunc key_ok,
188 KeyframeEditFunc key_cb,
189 FcuEditFunc fcu_cb)
190{
191 if (!slot) {
192 /* Valid situation, and will not have any FCurves. */
193 return 0;
194 }
195
197 for (FCurve *fcurve : fcurves) {
198 if (ANIM_fcurve_keyframes_loop(ked, fcurve, key_ok, key_cb, fcu_cb)) {
199 return 1;
200 }
201 }
202 return 0;
203}
204
205/* This function is used to loop over the keyframe data in an Action */
207 bAction *act,
208 KeyframeEditFunc key_ok,
209 KeyframeEditFunc key_cb,
210 FcuEditFunc fcu_cb)
211{
212 /* sanity check */
213 if (act == nullptr) {
214 return 0;
215 }
216
217 /* just loop through all F-Curves */
218 LISTBASE_FOREACH (FCurve *, fcu, &act->curves) {
219 if (ANIM_fcurve_keyframes_loop(ked, fcu, key_ok, key_cb, fcu_cb)) {
220 return 1;
221 }
222 }
223
224 return 0;
225}
226
227/* This function is used to loop over the keyframe data in an Object */
229 bDopeSheet *ads,
230 Object *ob,
231 KeyframeEditFunc key_ok,
232 KeyframeEditFunc key_cb,
233 FcuEditFunc fcu_cb)
234{
235 bAnimContext ac = {nullptr};
236 ListBase anim_data = {nullptr, nullptr};
237 int filter;
238 int ret = 0;
239
240 bAnimListElem dummy_chan = {nullptr};
241 Base dummy_base = {nullptr};
242
243 if (ob == nullptr) {
244 return 0;
245 }
246
247 /* create a dummy wrapper data to work with */
248 dummy_base.object = ob;
249
250 dummy_chan.type = ANIMTYPE_OBJECT;
251 dummy_chan.data = &dummy_base;
252 dummy_chan.id = &ob->id;
253 dummy_chan.adt = ob->adt;
254
255 ac.ads = ads;
256 ac.data = &dummy_chan;
260
261 /* get F-Curves to take keyframes from */
264 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
265
266 /* Loop through each F-Curve, applying the operation as required,
267 * but stopping on the first one. */
268 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
269 if (ANIM_fcurve_keyframes_loop(ked, static_cast<FCurve *>(ale->data), key_ok, key_cb, fcu_cb))
270 {
271 ret = 1;
272 break;
273 }
274 }
275
276 ANIM_animdata_freelist(&anim_data);
277
278 /* Return the return code (defaults to zero if nothing happened). */
279 return ret;
280}
281
282/* This function is used to loop over the keyframe data in a Scene */
284 bDopeSheet *ads,
285 Scene *sce,
286 KeyframeEditFunc key_ok,
287 KeyframeEditFunc key_cb,
288 FcuEditFunc fcu_cb)
289{
290 bAnimContext ac = {nullptr};
291 ListBase anim_data = {nullptr, nullptr};
292 int filter;
293 int ret = 0;
294
295 bAnimListElem dummy_chan = {nullptr};
296
297 if (sce == nullptr) {
298 return 0;
299 }
300
301 /* create a dummy wrapper data to work with */
302 dummy_chan.type = ANIMTYPE_SCENE;
303 dummy_chan.data = sce;
304 dummy_chan.id = &sce->id;
305 dummy_chan.adt = sce->adt;
306
307 ac.ads = ads;
308 ac.data = &dummy_chan;
312
313 /* get F-Curves to take keyframes from */
316 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
317
318 /* Loop through each F-Curve, applying the operation as required,
319 * but stopping on the first one. */
320 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
321 if (ANIM_fcurve_keyframes_loop(ked, static_cast<FCurve *>(ale->data), key_ok, key_cb, fcu_cb))
322 {
323 ret = 1;
324 break;
325 }
326 }
327
328 ANIM_animdata_freelist(&anim_data);
329
330 /* Return the return code (defaults to zero if nothing happened). */
331 return ret;
332}
333
334/* This function is used to loop over the keyframe data in a DopeSheet summary */
336 bAnimContext *ac,
337 KeyframeEditFunc key_ok,
338 KeyframeEditFunc key_cb,
339 FcuEditFunc fcu_cb)
340{
341 ListBase anim_data = {nullptr, nullptr};
342 int filter, ret_code = 0;
343
344 /* sanity check */
345 if (ac == nullptr) {
346 return 0;
347 }
348
349 /* get F-Curves to take keyframes from */
352 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
353
354 /* loop through each F-Curve, working on the keyframes until the first curve aborts */
355 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
356 switch (ale->datatype) {
357 case ALE_MASKLAY:
358 case ALE_GPFRAME:
360 break;
361
362 case ALE_FCURVE:
363 default: {
364 if (ked && ked->iterflags) {
365 /* make backups of the current values, so that a localized fix
366 * (e.g. NLA time remapping) can be applied to these values
367 */
368 float f1 = ked->f1;
369 float f2 = ked->f2;
370
371 if (ked->iterflags & KED_F1_NLA_UNMAP) {
373 }
374 if (ked->iterflags & KED_F2_NLA_UNMAP) {
376 }
377
378 /* now operate on the channel as per normal */
380 ked, static_cast<FCurve *>(ale->data), key_ok, key_cb, fcu_cb);
381
382 /* reset */
383 ked->f1 = f1;
384 ked->f2 = f2;
385 }
386 else {
387 /* no special handling required... */
389 ked, static_cast<FCurve *>(ale->data), key_ok, key_cb, fcu_cb);
390 }
391 break;
392 }
393 }
394
395 if (ret_code) {
396 break;
397 }
398 }
399
400 ANIM_animdata_freelist(&anim_data);
401
402 return ret_code;
403}
404
405/* --- */
406
408 bDopeSheet *ads,
409 bAnimListElem *ale,
410 KeyframeEditFunc key_ok,
411 KeyframeEditFunc key_cb,
412 FcuEditFunc fcu_cb)
413{
414 /* sanity checks */
415 if (ale == nullptr) {
416 return 0;
417 }
418
419 /* method to use depends on the type of keyframe data */
420 switch (ale->datatype) {
421 /* direct keyframe data (these loops are exposed) */
422 case ALE_FCURVE: /* F-Curve */
424 ked, static_cast<FCurve *>(ale->key_data), key_ok, key_cb, fcu_cb);
425
426 /* indirect 'summaries' (these are not exposed directly)
427 * NOTE: must keep this code in sync with the drawing code and also the filtering code!
428 */
429 case ALE_GROUP: /* action group */
430 return agrp_keyframes_loop(
431 ked, static_cast<bActionGroup *>(ale->data), key_ok, key_cb, fcu_cb);
432 case ALE_ACTION_LAYERED: { /* Layered Action. */
433 /* This assumes that the ALE_ACTION_LAYERED channel is shown in the dope-sheet context,
434 * underneath the data-block that owns `ale->adt`. So that means that the loop is limited to
435 * the keys that belong to that slot. */
436 animrig::Action &action = static_cast<bAction *>(ale->key_data)->wrap();
437 animrig::Slot *slot = action.slot_for_handle(ale->adt->slot_handle);
438 return action_layered_keyframes_loop(ked, action, slot, key_ok, key_cb, fcu_cb);
439 }
440 case ALE_ACTION_SLOT: {
441 animrig::Action *action = static_cast<animrig::Action *>(ale->key_data);
442 BLI_assert(action);
443 animrig::Slot *slot = static_cast<animrig::Slot *>(ale->data);
444 return action_layered_keyframes_loop(ked, *action, slot, key_ok, key_cb, fcu_cb);
445 }
446
447 case ALE_ACT: /* Legacy Action. */
449 ked, static_cast<bAction *>(ale->key_data), key_ok, key_cb, fcu_cb);
450 case ALE_OB: /* object */
451 return ob_keyframes_loop(
452 ked, ads, static_cast<Object *>(ale->key_data), key_ok, key_cb, fcu_cb);
453 case ALE_SCE: /* scene */
455 ked, ads, static_cast<Scene *>(ale->data), key_ok, key_cb, fcu_cb);
456 case ALE_ALL: /* 'all' (DopeSheet summary) */
458 ked, static_cast<bAnimContext *>(ale->data), key_ok, key_cb, fcu_cb);
459
460 case ALE_NONE:
461 case ALE_GPFRAME:
462 case ALE_MASKLAY:
463 case ALE_NLASTRIP:
467 break;
468 }
469
470 return 0;
471}
472
475 KeyframeEditFunc callback_fn)
476{
477 ListBase anim_data = {nullptr, nullptr};
478
480 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
481
482 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
484 static_cast<FCurve *>(ale->key_data),
485 nullptr,
486 callback_fn,
488 ale->update |= ANIM_UPDATE_DEFAULT;
489 }
490
491 ANIM_animdata_update(ac, &anim_data);
492 ANIM_animdata_freelist(&anim_data);
493}
494
495/* ************************************************************************** */
496/* Keyframe Integrity Tools */
497
499{
500 ListBase anim_data = {nullptr, nullptr};
501 int filter;
502
503 /* filter animation data */
506 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
507
508 /* Loop over F-Curves that are likely to have been edited, and tag them to
509 * ensure the keyframes are in order and handles are in a valid position. */
510 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
512 }
513
514 /* free temp data */
515 ANIM_animdata_update(ac, &anim_data);
516 ANIM_animdata_freelist(&anim_data);
517}
518
519/* ************************************************************************** */
520/* BezTriple Validation Callbacks */
521
522/* ------------------------ */
523
525{
526 const bool handles_shown = (ked->iterflags & KEYFRAME_ITER_HANDLES_INVISIBLE) == 0;
527 if (!handles_shown) {
528 return false;
529 }
530 const bool handles_shown_only_selected = ked->iterflags &
532
533 return handles_shown_only_selected ? BEZT_ISSEL_ANY(bezt) : true;
534}
535
537 KeyframeEditData *ked,
538 BezTriple *bezt,
539 blender::FunctionRef<bool(KeyframeEditData *ked, BezTriple *bezt, const int index)> check)
540{
541 short ok = 0;
542 if (check(ked, bezt, 1)) {
543 ok |= KEYFRAME_OK_KEY;
544 }
545 if (ked && (ked->iterflags & KEYFRAME_ITER_INCL_HANDLES))
546 { /* Only act on visible items, so check handle visibility state. */
547 if (handles_visible(ked, bezt)) {
548 if (check(ked, bezt, 0)) {
549 ok |= KEYFRAME_OK_H1;
550 }
551 if (check(ked, bezt, 2)) {
552 ok |= KEYFRAME_OK_H2;
553 }
554 }
555 }
556 return ok;
557}
558
559/* ------------------------ */
560
562{
563 /* frame is stored in f1 property (this float accuracy check may need to be dropped?) */
564 const short ok = keyframe_ok_checks(
565 ked, bezt, [](KeyframeEditData *ked, BezTriple *bezt, int index) -> bool {
566 return IS_EQF(bezt->vec[index][0], ked->f1);
567 });
568
569 return ok;
570}
571
573{
574 const short ok = keyframe_ok_checks(
575 ked, bezt, [](KeyframeEditData *ked, BezTriple *bezt, int index) -> bool {
576 return (bezt->vec[index][0] > ked->f1) && (bezt->vec[index][0] < ked->f2);
577 });
578
579 return ok;
580}
581
582static short ok_bezier_selected(KeyframeEditData * /*ked*/, BezTriple *bezt)
583{
584 /* this macro checks all beztriple handles for selection...
585 * only one of the verts has to be selected for this to be ok...
586 */
587 if (BEZT_ISSEL_ANY(bezt)) {
588 return KEYFRAME_OK_ALL;
589 }
590 return 0;
591}
592
594{
595 /* This macro checks the beztriple key (f2) selection. */
596 if (BEZT_ISSEL_IDX(bezt, 1)) {
597 return KEYFRAME_OK_KEY;
598 }
599 return 0;
600}
601
603{
604 /* Value is stored in f1 property:
605 * - This float accuracy check may need to be dropped?
606 * - Should value be stored in f2 instead
607 * so that we won't have conflicts when using f1 for frames too?
608 */
609 const short ok = keyframe_ok_checks(
610 ked, bezt, [](KeyframeEditData *ked, BezTriple *bezt, int index) -> bool {
611 return IS_EQF(bezt->vec[index][1], ked->f1);
612 });
613
614 return ok;
615}
616
618{
619 /* value range is stored in float properties */
620 const short ok = keyframe_ok_checks(
621 ked, bezt, [](KeyframeEditData *ked, BezTriple *bezt, int index) -> bool {
622 return (bezt->vec[index][1] > ked->f1) && (bezt->vec[index][1] < ked->f2);
623 });
624
625 return ok;
626}
627
629{
630 /* rect is stored in data property (it's of type rectf, but may not be set) */
631 if (!ked->data) {
632 return 0;
633 }
634
635 const short ok = keyframe_ok_checks(
636 ked, bezt, [](KeyframeEditData *ked, BezTriple *bezt, int index) -> bool {
637 return BLI_rctf_isect_pt_v(static_cast<rctf *>(ked->data), bezt->vec[index]);
638 });
639
640 return ok;
641}
642
643bool keyframe_region_lasso_test(const KeyframeEdit_LassoData *data_lasso, const float xy[2])
644{
645 if (BLI_rctf_isect_pt_v(data_lasso->rectf_scaled, xy)) {
646 float xy_view[2];
647
648 BLI_rctf_transform_pt_v(data_lasso->rectf_view, data_lasso->rectf_scaled, xy_view, xy);
649
650 if (BLI_lasso_is_point_inside(data_lasso->mcoords, xy_view[0], xy_view[1], INT_MAX)) {
651 return true;
652 }
653 }
654
655 return false;
656}
657
659{
660 /* check for lasso customdata (KeyframeEdit_LassoData) */
661 if (!ked->data) {
662 return 0;
663 }
664
665 const short ok = keyframe_ok_checks(
666 ked, bezt, [](KeyframeEditData *ked, BezTriple *bezt, int index) -> bool {
667 return keyframe_region_lasso_test(static_cast<KeyframeEdit_LassoData *>(ked->data),
668 bezt->vec[index]);
669 });
670
671 return ok;
672}
673
675{
676 /* check for lasso customdata (KeyframeEdit_LassoData) */
677 if (ked->data) {
679 float pt[2];
680
681 /* late-binding remap of the x values (for summary channels) */
682 /* XXX: Ideally we reset, but it should be fine just leaving it as-is
683 * as the next channel will reset it properly, while the next summary-channel
684 * curve will also reset by itself...
685 */
687 data->rectf_scaled->xmin = ked->f1;
688 data->rectf_scaled->xmax = ked->f2;
689 }
690
691 /* only use the x-coordinate of the point; the y is the channel range... */
692 pt[0] = bezt->vec[1][0];
693 pt[1] = ked->channel_y;
694
696 return KEYFRAME_OK_KEY;
697 }
698 }
699 return 0;
700}
701
702bool keyframe_region_circle_test(const KeyframeEdit_CircleData *data_circle, const float xy[2])
703{
704 if (BLI_rctf_isect_pt_v(data_circle->rectf_scaled, xy)) {
705 float xy_view[2];
706
707 BLI_rctf_transform_pt_v(data_circle->rectf_view, data_circle->rectf_scaled, xy_view, xy);
708
709 xy_view[0] = xy_view[0] - data_circle->mval[0];
710 xy_view[1] = xy_view[1] - data_circle->mval[1];
711 return len_squared_v2(xy_view) < data_circle->radius_squared;
712 }
713
714 return false;
715}
716
718{
719 /* check for circle select customdata (KeyframeEdit_CircleData) */
720 if (!ked->data) {
721 return 0;
722 }
723
724 const short ok = keyframe_ok_checks(
725 ked, bezt, [](KeyframeEditData *ked, BezTriple *bezt, int index) -> bool {
726 return keyframe_region_circle_test(static_cast<KeyframeEdit_CircleData *>(ked->data),
727 bezt->vec[index]);
728 });
729
730 return ok;
731}
732
734{
735 /* check for circle select customdata (KeyframeEdit_CircleData) */
736 if (ked->data) {
738 float pt[2];
739
740 /* late-binding remap of the x values (for summary channels) */
741 /* XXX: Ideally we reset, but it should be fine just leaving it as-is
742 * as the next channel will reset it properly, while the next summary-channel
743 * curve will also reset by itself...
744 */
746 data->rectf_scaled->xmin = ked->f1;
747 data->rectf_scaled->xmax = ked->f2;
748 }
749
750 /* only use the x-coordinate of the point; the y is the channel range... */
751 pt[0] = bezt->vec[1][0];
752 pt[1] = ked->channel_y;
753
755 return KEYFRAME_OK_KEY;
756 }
757 }
758 return 0;
759}
760
762{
763 /* eEditKeyframes_Validate */
764 switch (mode) {
765 case BEZT_OK_FRAME:
766 /* only if bezt falls on the right frame (float) */
767 return ok_bezier_frame;
769 /* only if bezt falls within the specified frame range (floats) */
771 case BEZT_OK_SELECTED:
772 /* only if bezt is selected (any of f1, f2, f3) */
773 return ok_bezier_selected;
775 /* only if bezt is selected (f2 is enough) */
777 case BEZT_OK_VALUE:
778 /* only if bezt value matches (float) */
779 return ok_bezier_value;
781 /* only if bezier falls within the specified value range (floats) */
783 case BEZT_OK_REGION:
784 /* only if bezier falls within the specified rect (data -> rectf) */
785 return ok_bezier_region;
787 /* only if the point falls within KeyframeEdit_LassoData defined data */
790 /* only if the point falls within KeyframeEdit_CircleData defined data */
793 /* same as BEZT_OK_REGION_LASSO, but we're only using the x-value of the points */
796 /* same as BEZT_OK_REGION_CIRCLE, but we're only using the x-value of the points */
798 default: /* nothing was ok */
799 return nullptr;
800 }
801}
802
803/* ******************************************* */
804/* Assorted Utility Functions */
805
807{
808 /* only if selected */
809 if (bezt->f2 & SELECT) {
810 /* store average time in float 1 (only do rounding at last step) */
811 ked->f1 += bezt->vec[1][0];
812
813 /* store average value in float 2 (only do rounding at last step)
814 * - this isn't always needed, but some operators may also require this
815 */
816 ked->f2 += bezt->vec[1][1];
817
818 /* increment number of items */
819 ked->i1++;
820 }
821
822 return 0;
823}
824
826{
827 /* only if selected */
828 if ((bezt->f2 & SELECT) == 0) {
829 return 0;
830 }
831
832 CfraElem *ce = MEM_callocN<CfraElem>("cfraElem");
833 BLI_addtail(&ked->list, ce);
834
835 /* bAnimListElem so we can do NLA mapping, we want the cfra to be in "global" time */
836 bAnimListElem *ale = static_cast<bAnimListElem *>(ked->data);
837 if (ale != nullptr) {
838 ce->cfra = ANIM_nla_tweakedit_remap(ale, bezt->vec[1][0], NLATIME_CONVERT_MAP);
839 }
840 else {
841 ce->cfra = bezt->vec[1][0];
842 }
843
844 return 0;
845}
846
848{
849 KeyframeEditCD_Remap *rmap = static_cast<KeyframeEditCD_Remap *>(ked->data);
850 const float scale = (rmap->newMax - rmap->newMin) / (rmap->oldMax - rmap->oldMin);
851
852 /* perform transform on all three handles unless indicated otherwise */
853 /* TODO: need to include some checks for that */
854
855 bezt->vec[0][0] = scale * (bezt->vec[0][0] - rmap->oldMin) + rmap->newMin;
856 bezt->vec[1][0] = scale * (bezt->vec[1][0] - rmap->oldMin) + rmap->newMin;
857 bezt->vec[2][0] = scale * (bezt->vec[2][0] - rmap->oldMin) + rmap->newMin;
858}
859
860/* ******************************************* */
861/* Transform */
862
863/* snaps the keyframe to the nearest frame */
864static short snap_bezier_nearest(KeyframeEditData * /*ked*/, BezTriple *bezt)
865{
866 if (bezt->f2 & SELECT) {
867 BKE_fcurve_keyframe_move_time_with_handles(bezt, floorf(bezt->vec[1][0] + 0.5f));
868 }
869 return 0;
870}
871
872/* snaps the keyframe to the nearest second */
874{
875 const Scene *scene = ked->scene;
876 const float secf = float(scene->frames_per_second());
877
878 if (bezt->f2 & SELECT) {
879 BKE_fcurve_keyframe_move_time_with_handles(bezt, floorf(bezt->vec[1][0] / secf + 0.5f) * secf);
880 }
881 return 0;
882}
883
884/* snaps the keyframe to the current frame */
886{
887 const Scene *scene = ked->scene;
888 if (bezt->f2 & SELECT) {
890 }
891 return 0;
892}
893
894/* snaps the keyframe time to the nearest marker's frame */
896{
897 if (bezt->f2 & SELECT) {
899 bezt, float(ED_markers_find_nearest_marker_time(&ked->list, bezt->vec[1][0])));
900 }
901 return 0;
902}
903
904/* make the handles have the same value as the key */
906{
907 if (bezt->f2 & SELECT) {
908 bezt->vec[0][1] = bezt->vec[2][1] = bezt->vec[1][1];
909
910 if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) {
911 bezt->h1 = HD_ALIGN;
912 }
913 if (ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) {
914 bezt->h2 = HD_ALIGN;
915 }
916 }
917 return 0;
918}
919
920/* frame to snap to is stored in the custom data -> first float value slot */
922{
923 if (bezt->f2 & SELECT) {
925 }
926 return 0;
927}
928
929/* value to snap to is stored in the custom data -> first float value slot */
931{
932 if (bezt->f2 & SELECT) {
934 }
935 return 0;
936}
937
939{
940 /* eEditKeyframes_Snap */
941 switch (mode) {
942 case SNAP_KEYS_NEARFRAME: /* snap to nearest frame */
943 return snap_bezier_nearest;
944 case SNAP_KEYS_CURFRAME: /* snap to current frame */
945 return snap_bezier_cframe;
946 case SNAP_KEYS_NEARMARKER: /* snap to nearest marker */
948 case SNAP_KEYS_NEARSEC: /* snap to nearest second */
950 case SNAP_KEYS_HORIZONTAL: /* snap handles to same value */
952 case SNAP_KEYS_TIME: /* snap to given frame/time */
953 return snap_bezier_time;
954 case SNAP_KEYS_VALUE: /* snap to given value */
955 return snap_bezier_value;
956 default: /* just in case */
957 return snap_bezier_nearest;
958 }
959}
960
961/* --------- */
962
963static void mirror_bezier_xaxis_ex(BezTriple *bezt, const float center)
964{
965 for (int i = 0; i < 3; i++) {
966 float diff = (center - bezt->vec[i][0]);
967 bezt->vec[i][0] = (center + diff);
968 }
969 swap_v3_v3(bezt->vec[0], bezt->vec[2]);
970
971 std::swap(bezt->h1, bezt->h2);
972 std::swap(bezt->f1, bezt->f3);
973}
974
975static void mirror_bezier_yaxis_ex(BezTriple *bezt, const float center)
976{
977 for (int i = 0; i < 3; i++) {
978 float diff = (center - bezt->vec[i][1]);
979 bezt->vec[i][1] = (center + diff);
980 }
981}
982
984{
985 const Scene *scene = ked->scene;
986
987 if (bezt->f2 & SELECT) {
988 mirror_bezier_xaxis_ex(bezt, scene->r.cfra);
989 }
990
991 return 0;
992}
993
994static short mirror_bezier_yaxis(KeyframeEditData * /*ked*/, BezTriple *bezt)
995{
996 if (bezt->f2 & SELECT) {
997 /* Yes, names are inverted, we are mirroring across y axis, hence along x axis... */
998 mirror_bezier_xaxis_ex(bezt, 0.0f);
999 }
1000
1001 return 0;
1002}
1003
1004static short mirror_bezier_xaxis(KeyframeEditData * /*ked*/, BezTriple *bezt)
1005{
1006 if (bezt->f2 & SELECT) {
1007 /* Yes, names are inverted, we are mirroring across x axis, hence along y axis... */
1008 mirror_bezier_yaxis_ex(bezt, 0.0f);
1009 }
1010
1011 return 0;
1012}
1013
1015{
1016 /* mirroring time stored in f1 */
1017 if (bezt->f2 & SELECT) {
1018 mirror_bezier_xaxis_ex(bezt, ked->f1);
1019 }
1020
1021 return 0;
1022}
1023
1025{
1026 /* value to mirror over is stored in f1 */
1027 if (bezt->f2 & SELECT) {
1028 mirror_bezier_xaxis_ex(bezt, ked->f1);
1029 }
1030
1031 return 0;
1032}
1033
1035{
1036 /* value to mirror over is stored in the custom data -> first float value slot */
1037 if (bezt->f2 & SELECT) {
1038 mirror_bezier_yaxis_ex(bezt, ked->f1);
1039 }
1040
1041 return 0;
1042}
1043
1045{
1046 switch (mode) {
1047 case MIRROR_KEYS_CURFRAME: /* mirror over current frame */
1048 return mirror_bezier_cframe;
1049 case MIRROR_KEYS_YAXIS: /* mirror over frame 0 */
1050 return mirror_bezier_yaxis;
1051 case MIRROR_KEYS_XAXIS: /* mirror over value 0 */
1052 return mirror_bezier_xaxis;
1053 case MIRROR_KEYS_MARKER: /* mirror over marker */
1054 return mirror_bezier_marker;
1055 case MIRROR_KEYS_TIME: /* mirror over frame/time */
1056 return mirror_bezier_time;
1057 case MIRROR_KEYS_VALUE: /* mirror over given value */
1058 return mirror_bezier_value;
1059 default: /* just in case */
1060 return mirror_bezier_yaxis;
1061 }
1062}
1063
1064/* ******************************************* */
1065/* Settings */
1066
1072#define ENSURE_HANDLES_MATCH(bezt) \
1073 if (bezt->h1 != bezt->h2) { \
1074 if (ELEM(bezt->h1, HD_ALIGN, HD_AUTO, HD_AUTO_ANIM)) { \
1075 bezt->h1 = HD_FREE; \
1076 } \
1077 if (ELEM(bezt->h2, HD_ALIGN, HD_AUTO, HD_AUTO_ANIM)) { \
1078 bezt->h2 = HD_FREE; \
1079 } \
1080 } \
1081 (void)0
1082
1083/* Sets the selected bezier handles to type 'auto' */
1084static short set_bezier_auto(KeyframeEditData * /*ked*/, BezTriple *bezt)
1085{
1086 /* If the key is selected, always apply to both handles. */
1087 if (bezt->f2 & SELECT) {
1088 bezt->h1 = bezt->h2 = HD_AUTO;
1089 }
1090 else {
1091 if (bezt->f1 & SELECT) {
1092 bezt->h1 = HD_AUTO;
1093 }
1094 if (bezt->f3 & SELECT) {
1095 bezt->h2 = HD_AUTO;
1096 }
1097
1099 }
1100
1101 return 0;
1102}
1103
1104/* Sets the selected bezier handles to type 'auto-clamped'
1105 * NOTE: this is like auto above, but they're handled a bit different
1106 */
1108{
1109 /* If the key is selected, always apply to both handles. */
1110 if (bezt->f2 & SELECT) {
1111 bezt->h1 = bezt->h2 = HD_AUTO_ANIM;
1112 }
1113 else {
1114 if (bezt->f1 & SELECT) {
1115 bezt->h1 = HD_AUTO_ANIM;
1116 }
1117 if (bezt->f3 & SELECT) {
1118 bezt->h2 = HD_AUTO_ANIM;
1119 }
1120
1122 }
1123
1124 return 0;
1125}
1126
1127/* Sets the selected bezier handles to type 'vector'. */
1128static short set_bezier_vector(KeyframeEditData * /*ked*/, BezTriple *bezt)
1129{
1130 /* If the key is selected, always apply to both handles. */
1131 if (bezt->f2 & SELECT) {
1132 bezt->h1 = bezt->h2 = HD_VECT;
1133 }
1134 else {
1135 if (bezt->f1 & SELECT) {
1136 bezt->h1 = HD_VECT;
1137 }
1138 if (bezt->f3 & SELECT) {
1139 bezt->h2 = HD_VECT;
1140 }
1141 }
1142
1143 return 0;
1144}
1145
1152static short bezier_isfree(KeyframeEditData * /*ked*/, BezTriple *bezt)
1153{
1154 if ((bezt->f1 & SELECT) && (bezt->h1)) {
1155 return 1;
1156 }
1157 if ((bezt->f3 & SELECT) && (bezt->h2)) {
1158 return 1;
1159 }
1160 return 0;
1161}
1162
1163/* Sets selected bezier handles to type 'align' */
1164static short set_bezier_align(KeyframeEditData * /*ked*/, BezTriple *bezt)
1165{
1166 /* If the key is selected, always apply to both handles. */
1167 if (bezt->f2 & SELECT) {
1168 bezt->h1 = bezt->h2 = HD_ALIGN;
1169 }
1170 else {
1171 if (bezt->f1 & SELECT) {
1172 bezt->h1 = HD_ALIGN;
1173 }
1174 if (bezt->f3 & SELECT) {
1175 bezt->h2 = HD_ALIGN;
1176 }
1177 }
1178
1179 return 0;
1180}
1181
1182/* Sets selected bezier handles to type 'free'. */
1183static short set_bezier_free(KeyframeEditData * /*ked*/, BezTriple *bezt)
1184{
1185 /* If the key is selected, always apply to both handles. */
1186 if (bezt->f2 & SELECT) {
1187 bezt->h1 = bezt->h2 = HD_FREE;
1188 }
1189 else {
1190 if (bezt->f1 & SELECT) {
1191 bezt->h1 = HD_FREE;
1192 }
1193 if (bezt->f3 & SELECT) {
1194 bezt->h2 = HD_FREE;
1195 }
1196 }
1197
1198 return 0;
1199}
1200
1202{
1203 switch (mode) {
1204 case HD_AUTO: /* auto */
1205 return set_bezier_auto;
1206 case HD_AUTO_ANIM: /* auto clamped */
1208
1209 case HD_VECT: /* vector */
1210 return set_bezier_vector;
1211 case HD_FREE: /* free */
1212 return set_bezier_free;
1213 case HD_ALIGN: /* align */
1214 return set_bezier_align;
1215
1216 default: /* check for toggle free or align? */
1217 return bezier_isfree;
1218 }
1219}
1220
1221/* ------- */
1222
1223static short set_bezt_constant(KeyframeEditData * /*ked*/, BezTriple *bezt)
1224{
1225 if (bezt->f2 & SELECT) {
1226 bezt->ipo = BEZT_IPO_CONST;
1227 }
1228 return 0;
1229}
1230
1231static short set_bezt_linear(KeyframeEditData * /*ked*/, BezTriple *bezt)
1232{
1233 if (bezt->f2 & SELECT) {
1234 bezt->ipo = BEZT_IPO_LIN;
1235 }
1236 return 0;
1237}
1238
1239static short set_bezt_bezier(KeyframeEditData * /*ked*/, BezTriple *bezt)
1240{
1241 if (bezt->f2 & SELECT) {
1242 bezt->ipo = BEZT_IPO_BEZ;
1243 }
1244 return 0;
1245}
1246
1247static short set_bezt_back(KeyframeEditData * /*ked*/, BezTriple *bezt)
1248{
1249 if (bezt->f2 & SELECT) {
1250 bezt->ipo = BEZT_IPO_BACK;
1251 }
1252 return 0;
1253}
1254
1255static short set_bezt_bounce(KeyframeEditData * /*ked*/, BezTriple *bezt)
1256{
1257 if (bezt->f2 & SELECT) {
1258 bezt->ipo = BEZT_IPO_BOUNCE;
1259 }
1260 return 0;
1261}
1262
1263static short set_bezt_circle(KeyframeEditData * /*ked*/, BezTriple *bezt)
1264{
1265 if (bezt->f2 & SELECT) {
1266 bezt->ipo = BEZT_IPO_CIRC;
1267 }
1268 return 0;
1269}
1270
1271static short set_bezt_cubic(KeyframeEditData * /*ked*/, BezTriple *bezt)
1272{
1273 if (bezt->f2 & SELECT) {
1274 bezt->ipo = BEZT_IPO_CUBIC;
1275 }
1276 return 0;
1277}
1278
1279static short set_bezt_elastic(KeyframeEditData * /*ked*/, BezTriple *bezt)
1280{
1281 if (bezt->f2 & SELECT) {
1282 bezt->ipo = BEZT_IPO_ELASTIC;
1283 }
1284 return 0;
1285}
1286
1287static short set_bezt_expo(KeyframeEditData * /*ked*/, BezTriple *bezt)
1288{
1289 if (bezt->f2 & SELECT) {
1290 bezt->ipo = BEZT_IPO_EXPO;
1291 }
1292 return 0;
1293}
1294
1295static short set_bezt_quad(KeyframeEditData * /*ked*/, BezTriple *bezt)
1296{
1297 if (bezt->f2 & SELECT) {
1298 bezt->ipo = BEZT_IPO_QUAD;
1299 }
1300 return 0;
1301}
1302
1303static short set_bezt_quart(KeyframeEditData * /*ked*/, BezTriple *bezt)
1304{
1305 if (bezt->f2 & SELECT) {
1306 bezt->ipo = BEZT_IPO_QUART;
1307 }
1308 return 0;
1309}
1310
1311static short set_bezt_quint(KeyframeEditData * /*ked*/, BezTriple *bezt)
1312{
1313 if (bezt->f2 & SELECT) {
1314 bezt->ipo = BEZT_IPO_QUINT;
1315 }
1316 return 0;
1317}
1318
1319static short set_bezt_sine(KeyframeEditData * /*ked*/, BezTriple *bezt)
1320{
1321 if (bezt->f2 & SELECT) {
1322 bezt->ipo = BEZT_IPO_SINE;
1323 }
1324 return 0;
1325}
1326
1327static void handle_flatten(float vec[3][3], const int idx, const float direction[2])
1328{
1329 BLI_assert_msg(idx == 0 || idx == 2, "handle_flatten() expects a handle index");
1330
1331 add_v2_v2v2(vec[idx], vec[1], direction);
1332}
1333
1334static void handle_set_length(float vec[3][3], const int idx, const float handle_length)
1335{
1336 BLI_assert_msg(idx == 0 || idx == 2, "handle_set_length() expects a handle index");
1337
1338 float handle_direction[2];
1339 sub_v2_v2v2(handle_direction, vec[idx], vec[1]);
1340 normalize_v2_length(handle_direction, handle_length);
1341 add_v2_v2v2(vec[idx], vec[1], handle_direction);
1342}
1343
1345 const eEditKeyframes_Equalize mode,
1346 const float handle_length,
1347 const bool flatten)
1348{
1349 uint i;
1350 BezTriple *bezt;
1351 const float flat_direction_left[2] = {-handle_length, 0.0f};
1352 const float flat_direction_right[2] = {handle_length, 0.0f};
1353
1354 /* Loop through an F-Curves keyframes. */
1355 for (bezt = fcu->bezt, i = 0; i < fcu->totvert; bezt++, i++) {
1356 if ((bezt->f2 & SELECT) == 0) {
1357 continue;
1358 }
1359
1360 /* Perform handle equalization if mode is 'Both' or 'Left'. */
1361 if (mode & EQUALIZE_HANDLES_LEFT) {
1362 /* If left handle type is 'Auto', 'Auto Clamped', or 'Vector', convert handles to
1363 * 'Aligned'.
1364 */
1365 if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) {
1366 bezt->h1 = HD_ALIGN;
1367 bezt->h2 = HD_ALIGN;
1368 }
1369
1370 if (flatten) {
1371 handle_flatten(bezt->vec, 0, flat_direction_left);
1372 }
1373 else {
1374 handle_set_length(bezt->vec, 0, handle_length);
1375 }
1376 }
1377
1378 /* Perform handle equalization if mode is 'Both' or 'Right'. */
1379 if (mode & EQUALIZE_HANDLES_RIGHT) {
1380 /* If right handle type is 'Auto', 'Auto Clamped', or 'Vector', convert handles to
1381 * 'Aligned'. */
1382 if (ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) {
1383 bezt->h1 = HD_ALIGN;
1384 bezt->h2 = HD_ALIGN;
1385 }
1386
1387 if (flatten) {
1388 handle_flatten(bezt->vec, 2, flat_direction_right);
1389 }
1390 else {
1391 handle_set_length(bezt->vec, 2, handle_length);
1392 }
1393 }
1394 }
1395}
1396
1398{
1399 switch (mode) {
1400 /* interpolation */
1401 case BEZT_IPO_CONST: /* constant */
1402 return set_bezt_constant;
1403 case BEZT_IPO_LIN: /* linear */
1404 return set_bezt_linear;
1405
1406 /* easing */
1407 case BEZT_IPO_BACK:
1408 return set_bezt_back;
1409 case BEZT_IPO_BOUNCE:
1410 return set_bezt_bounce;
1411 case BEZT_IPO_CIRC:
1412 return set_bezt_circle;
1413 case BEZT_IPO_CUBIC:
1414 return set_bezt_cubic;
1415 case BEZT_IPO_ELASTIC:
1416 return set_bezt_elastic;
1417 case BEZT_IPO_EXPO:
1418 return set_bezt_expo;
1419 case BEZT_IPO_QUAD:
1420 return set_bezt_quad;
1421 case BEZT_IPO_QUART:
1422 return set_bezt_quart;
1423 case BEZT_IPO_QUINT:
1424 return set_bezt_quint;
1425 case BEZT_IPO_SINE:
1426 return set_bezt_sine;
1427
1428 default: /* bezier */
1429 return set_bezt_bezier;
1430 }
1431}
1432
1433/* ------- */
1434
1436{
1437 if (bezt->f2 & SELECT) {
1439 }
1440 return 0;
1441}
1442
1444{
1445 if (bezt->f2 & SELECT) {
1447 }
1448 return 0;
1449}
1450
1451static short set_keytype_extreme(KeyframeEditData * /*ked*/, BezTriple *bezt)
1452{
1453 if (bezt->f2 & SELECT) {
1455 }
1456 return 0;
1457}
1458
1459static short set_keytype_jitter(KeyframeEditData * /*ked*/, BezTriple *bezt)
1460{
1461 if (bezt->f2 & SELECT) {
1463 }
1464 return 0;
1465}
1466
1468{
1469 if (bezt->f2 & SELECT) {
1471 }
1472 return 0;
1473}
1474
1476{
1477 if (bezt->f2 & SELECT) {
1479 }
1480 return 0;
1481}
1482
1484{
1485 switch (keyframe_type) {
1487 return set_keytype_breakdown;
1488
1490 return set_keytype_extreme;
1491
1493 return set_keytype_jitter;
1494
1497
1499 return set_keytype_keyframe;
1500
1502 return set_keytype_generated;
1503 }
1504
1506 return nullptr;
1507}
1508
1509/* ------- */
1510
1512{
1513 if (bezt->f2 & SELECT) {
1514 bezt->easing = BEZT_IPO_EASE_IN;
1515 }
1516 return 0;
1517}
1518
1520{
1521 if (bezt->f2 & SELECT) {
1522 bezt->easing = BEZT_IPO_EASE_OUT;
1523 }
1524 return 0;
1525}
1526
1528{
1529 if (bezt->f2 & SELECT) {
1531 }
1532 return 0;
1533}
1534
1536{
1537 if (bezt->f2 & SELECT) {
1538 bezt->easing = BEZT_IPO_EASE_AUTO;
1539 }
1540 return 0;
1541}
1542
1544{
1545 switch (mode) {
1546 case BEZT_IPO_EASE_IN: /* ease in */
1547 return set_easingtype_easein;
1548
1549 case BEZT_IPO_EASE_OUT: /* ease out */
1551
1552 case BEZT_IPO_EASE_IN_OUT: /* both */
1554
1555 default: /* auto */
1557 }
1558}
1559
1560/* ******************************************* */
1561/* Selection */
1562
1564{
1565 /* Only act on visible items, so check handle visibility state. */
1566 /* if we've got info on what to select, use it, otherwise select all */
1567 if ((ked) && (ked->iterflags & KEYFRAME_ITER_INCL_HANDLES) && handles_visible(ked, bezt)) {
1568 if (ked->curflags & KEYFRAME_OK_KEY) {
1569 bezt->f2 |= SELECT;
1570 }
1571 if (ked->curflags & KEYFRAME_OK_H1) {
1572 bezt->f1 |= SELECT;
1573 }
1574 if (ked->curflags & KEYFRAME_OK_H2) {
1575 bezt->f3 |= SELECT;
1576 }
1577 }
1578 else {
1579 BEZT_SEL_ALL(bezt);
1580 }
1581
1582 return 0;
1583}
1584
1586{
1587 /* Only act on visible items, so check handle visibility state. */
1588 /* if we've got info on what to deselect, use it, otherwise deselect all */
1589 if ((ked) && (ked->iterflags & KEYFRAME_ITER_INCL_HANDLES) && handles_visible(ked, bezt)) {
1590 if (ked->curflags & KEYFRAME_OK_KEY) {
1591 bezt->f2 &= ~SELECT;
1592 }
1593 if (ked->curflags & KEYFRAME_OK_H1) {
1594 bezt->f1 &= ~SELECT;
1595 }
1596 if (ked->curflags & KEYFRAME_OK_H2) {
1597 bezt->f3 &= ~SELECT;
1598 }
1599 }
1600 else {
1601 BEZT_DESEL_ALL(bezt);
1602 }
1603
1604 return 0;
1605}
1606
1608{
1609 /* Invert the selection for the whole bezier triple */
1610 bezt->f2 ^= SELECT;
1611 if (bezt->f2 & SELECT) {
1612 bezt->f1 |= SELECT;
1613 bezt->f3 |= SELECT;
1614 }
1615 else {
1616 bezt->f1 &= ~SELECT;
1617 bezt->f3 &= ~SELECT;
1618 }
1619 return 0;
1620}
1621
1623{
1624 switch (selectmode) {
1625 case SELECT_ADD: /* add */
1626 return select_bezier_add;
1627 case SELECT_SUBTRACT: /* subtract */
1629 case SELECT_INVERT: /* invert */
1630 return select_bezier_invert;
1631 default: /* replace (need to clear all, then add) */
1632 return select_bezier_add;
1633 }
1634}
1635
1636/* ******************************************* */
1637/* Selection Maps */
1638
1639/* Selection maps are simply fancy names for char arrays that store on/off
1640 * info for whether the selection status. The main purpose for these is to
1641 * allow extra info to be tagged to the keyframes without influencing their
1642 * values or having to be removed later.
1643 */
1644
1645/* ----------- */
1646
1648{
1649 const FCurve *fcu = ked->fcu;
1650 char *map = static_cast<char *>(ked->data);
1651 int i = ked->curIndex;
1652
1653 /* if current is selected, just make sure it stays this way */
1654 if (BEZT_ISSEL_ANY(bezt)) {
1655 map[i] = 1;
1656 return 0;
1657 }
1658
1659 /* if previous is selected, that means that selection should extend across */
1660 if (i > 0) {
1661 BezTriple *prev = bezt - 1;
1662
1663 if (BEZT_ISSEL_ANY(prev)) {
1664 map[i] = 1;
1665 return 0;
1666 }
1667 }
1668
1669 /* if next is selected, that means that selection should extend across */
1670 if (i < (fcu->totvert - 1)) {
1671 BezTriple *next = bezt + 1;
1672
1673 if (BEZT_ISSEL_ANY(next)) {
1674 map[i] = 1;
1675 return 0;
1676 }
1677 }
1678
1679 return 0;
1680}
1681
1683{
1684 const FCurve *fcu = ked->fcu;
1685 char *map = static_cast<char *>(ked->data);
1686 int i = ked->curIndex;
1687
1688 /* if current is selected, check the left/right keyframes
1689 * since it might need to be deselected (but otherwise no)
1690 */
1691 if (BEZT_ISSEL_ANY(bezt)) {
1692 /* if previous is not selected, we're on the tip of an iceberg */
1693 if (i > 0) {
1694 BezTriple *prev = bezt - 1;
1695
1696 if (BEZT_ISSEL_ANY(prev) == 0) {
1697 return 0;
1698 }
1699 }
1700 else if (i == 0) {
1701 /* current keyframe is selected at an endpoint, so should get deselected */
1702 return 0;
1703 }
1704
1705 /* if next is not selected, we're on the tip of an iceberg */
1706 if (i < (fcu->totvert - 1)) {
1707 BezTriple *next = bezt + 1;
1708
1709 if (BEZT_ISSEL_ANY(next) == 0) {
1710 return 0;
1711 }
1712 }
1713 else if (i == (fcu->totvert - 1)) {
1714 /* current keyframe is selected at an endpoint, so should get deselected */
1715 return 0;
1716 }
1717
1718 /* if we're still here, that means that keyframe should remain untouched */
1719 map[i] = 1;
1720 }
1721
1722 return 0;
1723}
1724
1726{
1727 switch (mode) {
1728 case SELMAP_LESS: /* less */
1730
1731 case SELMAP_MORE: /* more */
1732 default:
1734 }
1735}
1736
1737/* ----------- */
1738
1740{
1741 const char *map = static_cast<char *>(ked->data);
1742 short on = map[ked->curIndex];
1743
1744 /* select or deselect based on whether the map allows it or not */
1745 if (on) {
1746 BEZT_SEL_ALL(bezt);
1747 }
1748 else {
1749 BEZT_DESEL_ALL(bezt);
1750 }
1751
1752 return 0;
1753}
Functions and classes to work with Actions.
void BKE_fcurve_keyframe_move_time_with_handles(BezTriple *keyframe, const float new_time)
void BKE_fcurve_handles_recalc(FCurve *fcu)
void BKE_fcurve_keyframe_move_value_with_handles(BezTriple *keyframe, float new_value)
@ NLATIME_CONVERT_MAP
Definition BKE_nla.hh:552
@ NLATIME_CONVERT_UNMAP
Definition BKE_nla.hh:549
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
bool BLI_lasso_is_point_inside(blender::Span< blender::int2 > mcoords, int sx, int sy, int error_value)
#define LISTBASE_FOREACH(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
MINLINE float len_squared_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v2_length(float n[2], float unit_length)
MINLINE void add_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void swap_v3_v3(float a[3], float b[3])
bool BLI_rctf_isect_pt_v(const struct rctf *rect, const float xy[2])
void BLI_rctf_transform_pt_v(const rctf *dst, const rctf *src, float xy_dst[2], const float xy_src[2])
Definition rct.cc:526
unsigned int uint
#define ELEM(...)
#define IS_EQF(a, b)
eDopeSheet_FilterFlag2
eDopeSheet_FilterFlag
@ HD_AUTO_ANIM
@ HD_VECT
@ HD_FREE
@ HD_AUTO
@ HD_ALIGN
@ BEZT_IPO_ELASTIC
@ BEZT_IPO_CIRC
@ BEZT_IPO_QUART
@ BEZT_IPO_BACK
@ BEZT_IPO_BOUNCE
@ BEZT_IPO_CUBIC
@ BEZT_IPO_EXPO
@ BEZT_IPO_CONST
@ BEZT_IPO_BEZ
@ BEZT_IPO_LIN
@ BEZT_IPO_SINE
@ BEZT_IPO_QUAD
@ BEZT_IPO_QUINT
@ BEZT_IPO_EASE_OUT
@ BEZT_IPO_EASE_AUTO
@ BEZT_IPO_EASE_IN
@ BEZT_IPO_EASE_IN_OUT
eBezTriple_KeyframeType
@ BEZT_KEYTYPE_EXTREME
@ BEZT_KEYTYPE_JITTER
@ BEZT_KEYTYPE_BREAKDOWN
@ BEZT_KEYTYPE_MOVEHOLD
@ BEZT_KEYTYPE_GENERATED
@ BEZT_KEYTYPE_KEYFRAME
#define BEZT_ISSEL_IDX(bezt, i)
#define BEZT_SEL_ALL(bezt)
#define BEZT_ISSEL_ANY(bezt)
#define BEZT_DESEL_ALL(bezt)
#define BEZKEYTYPE_LVALUE(bezt)
Object is a sort of wrapper for general info.
@ ANIMTYPE_SCENE
@ ANIMTYPE_OBJECT
#define ANIM_UPDATE_DEFAULT
@ ALE_GREASE_PENCIL_GROUP
@ ALE_SCE
@ ALE_GREASE_PENCIL_CEL
@ ALE_GREASE_PENCIL_DATA
@ ALE_NONE
@ ALE_GPFRAME
@ ALE_FCURVE
@ ALE_NLASTRIP
@ ALE_ALL
@ ALE_ACT
@ ALE_ACTION_LAYERED
@ ALE_OB
@ ALE_GROUP
@ ALE_ACTION_SLOT
@ ALE_MASKLAY
@ ANIM_UPDATE_DEPS
@ ANIM_UPDATE_HANDLES
@ ANIM_UPDATE_ORDER
eAnimCont_Types
@ ANIMCONT_CHANNEL
eAnimFilter_Flags
@ ANIMFILTER_DATA_VISIBLE
@ ANIMFILTER_FCURVESONLY
@ MIRROR_KEYS_VALUE
@ MIRROR_KEYS_YAXIS
@ MIRROR_KEYS_MARKER
@ MIRROR_KEYS_CURFRAME
@ MIRROR_KEYS_XAXIS
@ MIRROR_KEYS_TIME
eEditKeyframes_Equalize
@ EQUALIZE_HANDLES_LEFT
@ EQUALIZE_HANDLES_RIGHT
eKeyframeVertOk
@ KEYFRAME_NONE
@ KEYFRAME_OK_KEY
@ KEYFRAME_OK_H1
@ KEYFRAME_OK_H2
@ KEYFRAME_OK_ALL
@ BEZT_OK_CHANNEL_CIRCLE
@ BEZT_OK_FRAMERANGE
@ BEZT_OK_FRAME
@ BEZT_OK_SELECTED_KEY
@ BEZT_OK_VALUERANGE
@ BEZT_OK_SELECTED
@ BEZT_OK_REGION_LASSO
@ BEZT_OK_VALUE
@ BEZT_OK_REGION_CIRCLE
@ BEZT_OK_CHANNEL_LASSO
@ BEZT_OK_REGION
@ KEYFRAME_ITER_HANDLES_DEFAULT_INVISIBLE
@ KED_F1_NLA_UNMAP
@ KEYFRAME_ITER_INCL_HANDLES
@ KEYFRAME_ITER_HANDLES_INVISIBLE
@ KED_F2_NLA_UNMAP
@ SNAP_KEYS_CURFRAME
@ SNAP_KEYS_NEARFRAME
@ SNAP_KEYS_NEARMARKER
@ SNAP_KEYS_TIME
@ SNAP_KEYS_NEARSEC
@ SNAP_KEYS_HORIZONTAL
@ SNAP_KEYS_VALUE
short(*)(KeyframeEditData *ked, BezTriple *bezt) KeyframeEditFunc
@ SELMAP_MORE
@ SELMAP_LESS
eEditKeyframes_Select
@ SELECT_INVERT
@ SELECT_SUBTRACT
@ SELECT_ADD
void(*)(FCurve *fcu) FcuEditFunc
Read Guarded memory(de)allocation.
void ANIM_animdata_freelist(ListBase *anim_data)
Definition anim_deps.cc:463
void ANIM_animdata_update(bAnimContext *ac, ListBase *anim_data)
Definition anim_deps.cc:356
float ANIM_nla_tweakedit_remap(bAnimListElem *ale, const float cframe, const eNlaTime_ConvertModes mode)
Definition anim_draw.cc:324
size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, const eAnimFilter_Flags filter_mode, void *data, const eAnimCont_Types datatype)
int ED_markers_find_nearest_marker_time(ListBase *markers, float x)
BMesh const char void * data
Slot * slot_for_handle(slot_handle_t handle)
blender::Span< const FCurve * > fcurves() const
nullptr float
#define SELECT
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
Definition frames.inl:1166
#define filter
static short mirror_bezier_xaxis(KeyframeEditData *, BezTriple *bezt)
static short ok_bezier_channel_circle(KeyframeEditData *ked, BezTriple *bezt)
static short summary_keyframes_loop(KeyframeEditData *ked, bAnimContext *ac, KeyframeEditFunc key_ok, KeyframeEditFunc key_cb, FcuEditFunc fcu_cb)
static short ok_bezier_selected(KeyframeEditData *, BezTriple *bezt)
static short set_bezier_auto_clamped(KeyframeEditData *, BezTriple *bezt)
static void mirror_bezier_xaxis_ex(BezTriple *bezt, const float center)
static short ok_bezier_framerange(KeyframeEditData *ked, BezTriple *bezt)
static short set_bezt_circle(KeyframeEditData *, BezTriple *bezt)
KeyframeEditFunc ANIM_editkeyframes_mirror(short mode)
static short keyframe_ok_checks(KeyframeEditData *ked, BezTriple *bezt, blender::FunctionRef< bool(KeyframeEditData *ked, BezTriple *bezt, const int index)> check)
short bezt_selmap_flush(KeyframeEditData *ked, BezTriple *bezt)
#define ENSURE_HANDLES_MATCH(bezt)
static short set_easingtype_easeout(KeyframeEditData *, BezTriple *bezt)
void ANIM_editkeyframes_refresh(bAnimContext *ac)
static short set_bezt_quart(KeyframeEditData *, BezTriple *bezt)
static short set_bezt_expo(KeyframeEditData *, BezTriple *bezt)
KeyframeEditFunc ANIM_editkeyframes_easing(short mode)
static short set_bezier_vector(KeyframeEditData *, BezTriple *bezt)
static short set_easingtype_easeauto(KeyframeEditData *, BezTriple *bezt)
static short set_bezt_bounce(KeyframeEditData *, BezTriple *bezt)
static short snap_bezier_nearest(KeyframeEditData *, BezTriple *bezt)
static short select_bezier_subtract(KeyframeEditData *ked, BezTriple *bezt)
static short snap_bezier_value(KeyframeEditData *ked, BezTriple *bezt)
KeyframeEditFunc ANIM_editkeyframes_ipo(short mode)
static short mirror_bezier_value(KeyframeEditData *ked, BezTriple *bezt)
static short set_keytype_generated(KeyframeEditData *, BezTriple *bezt)
static short selmap_build_bezier_more(KeyframeEditData *ked, BezTriple *bezt)
bool keyframe_region_lasso_test(const KeyframeEdit_LassoData *data_lasso, const float xy[2])
static short set_keytype_extreme(KeyframeEditData *, BezTriple *bezt)
static short ok_bezier_region_circle(KeyframeEditData *ked, BezTriple *bezt)
static short set_bezt_elastic(KeyframeEditData *, BezTriple *bezt)
static short select_bezier_add(KeyframeEditData *ked, BezTriple *bezt)
static short snap_bezier_nearmarker(KeyframeEditData *ked, BezTriple *bezt)
static short scene_keyframes_loop(KeyframeEditData *ked, bDopeSheet *ads, Scene *sce, KeyframeEditFunc key_ok, KeyframeEditFunc key_cb, FcuEditFunc fcu_cb)
static short set_keytype_breakdown(KeyframeEditData *, BezTriple *bezt)
static void handle_set_length(float vec[3][3], const int idx, const float handle_length)
void ANIM_fcurve_equalize_keyframes_loop(FCurve *fcu, const eEditKeyframes_Equalize mode, const float handle_length, const bool flatten)
static short set_keytype_moving_hold(KeyframeEditData *, BezTriple *bezt)
static short snap_bezier_time(KeyframeEditData *ked, BezTriple *bezt)
static short action_legacy_keyframes_loop(KeyframeEditData *ked, bAction *act, KeyframeEditFunc key_ok, KeyframeEditFunc key_cb, FcuEditFunc fcu_cb)
static short ok_bezier_selected_key(KeyframeEditData *, BezTriple *bezt)
static short select_bezier_invert(KeyframeEditData *, BezTriple *bezt)
static void handle_flatten(float vec[3][3], const int idx, const float direction[2])
static short set_bezier_auto(KeyframeEditData *, BezTriple *bezt)
static short set_bezt_quint(KeyframeEditData *, BezTriple *bezt)
static short set_bezt_quad(KeyframeEditData *, BezTriple *bezt)
static short set_bezt_bezier(KeyframeEditData *, BezTriple *bezt)
static short snap_bezier_horizontal(KeyframeEditData *, BezTriple *bezt)
static short ok_bezier_frame(KeyframeEditData *ked, BezTriple *bezt)
static short set_bezt_back(KeyframeEditData *, BezTriple *bezt)
static short set_bezier_free(KeyframeEditData *, BezTriple *bezt)
static short set_bezt_sine(KeyframeEditData *, BezTriple *bezt)
static short set_keytype_jitter(KeyframeEditData *, BezTriple *bezt)
static short set_easingtype_easein(KeyframeEditData *, BezTriple *bezt)
short bezt_to_cfraelem(KeyframeEditData *ked, BezTriple *bezt)
static short snap_bezier_cframe(KeyframeEditData *ked, BezTriple *bezt)
KeyframeEditFunc ANIM_editkeyframes_buildselmap(short mode)
static short set_bezier_align(KeyframeEditData *, BezTriple *bezt)
static short agrp_keyframes_loop(KeyframeEditData *ked, bActionGroup *agrp, KeyframeEditFunc key_ok, KeyframeEditFunc key_cb, FcuEditFunc fcu_cb)
static short mirror_bezier_cframe(KeyframeEditData *ked, BezTriple *bezt)
short ANIM_animchannel_keyframes_loop(KeyframeEditData *ked, bDopeSheet *ads, bAnimListElem *ale, KeyframeEditFunc key_ok, KeyframeEditFunc key_cb, FcuEditFunc fcu_cb)
static void mirror_bezier_yaxis_ex(BezTriple *bezt, const float center)
bool keyframe_region_circle_test(const KeyframeEdit_CircleData *data_circle, const float xy[2])
void bezt_remap_times(KeyframeEditData *ked, BezTriple *bezt)
short ANIM_fcurve_keyframes_loop(KeyframeEditData *ked, FCurve *fcu, KeyframeEditFunc key_ok, KeyframeEditFunc key_cb, FcuEditFunc fcu_cb)
static bool handles_visible(KeyframeEditData *ked, BezTriple *bezt)
static short bezier_isfree(KeyframeEditData *, BezTriple *bezt)
KeyframeEditFunc ANIM_editkeyframes_keytype(const eBezTriple_KeyframeType keyframe_type)
static short set_bezt_cubic(KeyframeEditData *, BezTriple *bezt)
KeyframeEditFunc ANIM_editkeyframes_ok(short mode)
static short set_bezt_constant(KeyframeEditData *, BezTriple *bezt)
KeyframeEditFunc ANIM_editkeyframes_snap(short mode)
static short ob_keyframes_loop(KeyframeEditData *ked, bDopeSheet *ads, Object *ob, KeyframeEditFunc key_ok, KeyframeEditFunc key_cb, FcuEditFunc fcu_cb)
static short snap_bezier_nearestsec(KeyframeEditData *ked, BezTriple *bezt)
static short set_easingtype_easeinout(KeyframeEditData *, BezTriple *bezt)
static short mirror_bezier_marker(KeyframeEditData *ked, BezTriple *bezt)
static short action_layered_keyframes_loop(KeyframeEditData *ked, animrig::Action &action, animrig::Slot *slot, KeyframeEditFunc key_ok, KeyframeEditFunc key_cb, FcuEditFunc fcu_cb)
static short selmap_build_bezier_less(KeyframeEditData *ked, BezTriple *bezt)
static short ok_bezier_region(KeyframeEditData *ked, BezTriple *bezt)
void ANIM_animdata_keyframe_callback(bAnimContext *ac, eAnimFilter_Flags filter, KeyframeEditFunc callback_fn)
short bezt_calc_average(KeyframeEditData *ked, BezTriple *bezt)
static short set_bezt_linear(KeyframeEditData *, BezTriple *bezt)
static short mirror_bezier_yaxis(KeyframeEditData *, BezTriple *bezt)
static short ok_bezier_valuerange(KeyframeEditData *ked, BezTriple *bezt)
static short mirror_bezier_time(KeyframeEditData *ked, BezTriple *bezt)
static short ok_bezier_channel_lasso(KeyframeEditData *ked, BezTriple *bezt)
KeyframeEditFunc ANIM_editkeyframes_select(const eEditKeyframes_Select selectmode)
static short set_keytype_keyframe(KeyframeEditData *, BezTriple *bezt)
static short ok_bezier_value(KeyframeEditData *ked, BezTriple *bezt)
static short ok_bezier_region_lasso(KeyframeEditData *ked, BezTriple *bezt)
KeyframeEditFunc ANIM_editkeyframes_handles(short mode)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
static ulong * next
Span< FCurve * > fcurves_for_action_slot(Action &action, slot_handle_t slot_handle)
float wrap(float value, float max, float min)
Definition node_math.h:103
return ret
#define floorf
int32_t slot_handle
struct Object * object
float vec[3][3]
BezTriple * bezt
unsigned int totvert
eKeyframeIterFlags iterflags
eKeyframeVertOk curflags
blender::Array< blender::int2 > mcoords
struct AnimData * adt
struct RenderData r
struct AnimData * adt
struct ActionChannelbag * channelbag
ListBase curves
eDopeSheet_FilterFlag flag
eAnimCont_Types datatype
bDopeSheet * ads
struct bAnimContext::@242276144047124000362312251357014355232301013033 filters
eDopeSheet_FilterFlag2 flag2
AnimData * adt
eAnim_ChannelType type
eAnim_KeyType datatype
i
Definition text_draw.cc:230
int xy[2]
Definition wm_draw.cc:178