Blender V4.5
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;
258
259 /* get F-Curves to take keyframes from */
262 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
263
264 /* Loop through each F-Curve, applying the operation as required,
265 * but stopping on the first one. */
266 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
267 if (ANIM_fcurve_keyframes_loop(ked, static_cast<FCurve *>(ale->data), key_ok, key_cb, fcu_cb))
268 {
269 ret = 1;
270 break;
271 }
272 }
273
274 ANIM_animdata_freelist(&anim_data);
275
276 /* Return the return code (defaults to zero if nothing happened). */
277 return ret;
278}
279
280/* This function is used to loop over the keyframe data in a Scene */
282 bDopeSheet *ads,
283 Scene *sce,
284 KeyframeEditFunc key_ok,
285 KeyframeEditFunc key_cb,
286 FcuEditFunc fcu_cb)
287{
288 bAnimContext ac = {nullptr};
289 ListBase anim_data = {nullptr, nullptr};
290 int filter;
291 int ret = 0;
292
293 bAnimListElem dummy_chan = {nullptr};
294
295 if (sce == nullptr) {
296 return 0;
297 }
298
299 /* create a dummy wrapper data to work with */
300 dummy_chan.type = ANIMTYPE_SCENE;
301 dummy_chan.data = sce;
302 dummy_chan.id = &sce->id;
303 dummy_chan.adt = sce->adt;
304
305 ac.ads = ads;
306 ac.data = &dummy_chan;
308
309 /* get F-Curves to take keyframes from */
312 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
313
314 /* Loop through each F-Curve, applying the operation as required,
315 * but stopping on the first one. */
316 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
317 if (ANIM_fcurve_keyframes_loop(ked, static_cast<FCurve *>(ale->data), key_ok, key_cb, fcu_cb))
318 {
319 ret = 1;
320 break;
321 }
322 }
323
324 ANIM_animdata_freelist(&anim_data);
325
326 /* Return the return code (defaults to zero if nothing happened). */
327 return ret;
328}
329
330/* This function is used to loop over the keyframe data in a DopeSheet summary */
332 bAnimContext *ac,
333 KeyframeEditFunc key_ok,
334 KeyframeEditFunc key_cb,
335 FcuEditFunc fcu_cb)
336{
337 ListBase anim_data = {nullptr, nullptr};
338 int filter, ret_code = 0;
339
340 /* sanity check */
341 if (ac == nullptr) {
342 return 0;
343 }
344
345 /* get F-Curves to take keyframes from */
348 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
349
350 /* loop through each F-Curve, working on the keyframes until the first curve aborts */
351 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
352 switch (ale->datatype) {
353 case ALE_MASKLAY:
354 case ALE_GPFRAME:
356 break;
357
358 case ALE_FCURVE:
359 default: {
360 if (ked && ked->iterflags) {
361 /* make backups of the current values, so that a localized fix
362 * (e.g. NLA time remapping) can be applied to these values
363 */
364 float f1 = ked->f1;
365 float f2 = ked->f2;
366
367 if (ked->iterflags & KED_F1_NLA_UNMAP) {
369 }
370 if (ked->iterflags & KED_F2_NLA_UNMAP) {
372 }
373
374 /* now operate on the channel as per normal */
376 ked, static_cast<FCurve *>(ale->data), key_ok, key_cb, fcu_cb);
377
378 /* reset */
379 ked->f1 = f1;
380 ked->f2 = f2;
381 }
382 else {
383 /* no special handling required... */
385 ked, static_cast<FCurve *>(ale->data), key_ok, key_cb, fcu_cb);
386 }
387 break;
388 }
389 }
390
391 if (ret_code) {
392 break;
393 }
394 }
395
396 ANIM_animdata_freelist(&anim_data);
397
398 return ret_code;
399}
400
401/* --- */
402
404 bDopeSheet *ads,
405 bAnimListElem *ale,
406 KeyframeEditFunc key_ok,
407 KeyframeEditFunc key_cb,
408 FcuEditFunc fcu_cb)
409{
410 /* sanity checks */
411 if (ale == nullptr) {
412 return 0;
413 }
414
415 /* method to use depends on the type of keyframe data */
416 switch (ale->datatype) {
417 /* direct keyframe data (these loops are exposed) */
418 case ALE_FCURVE: /* F-Curve */
420 ked, static_cast<FCurve *>(ale->key_data), key_ok, key_cb, fcu_cb);
421
422 /* indirect 'summaries' (these are not exposed directly)
423 * NOTE: must keep this code in sync with the drawing code and also the filtering code!
424 */
425 case ALE_GROUP: /* action group */
426 return agrp_keyframes_loop(
427 ked, static_cast<bActionGroup *>(ale->data), key_ok, key_cb, fcu_cb);
428 case ALE_ACTION_LAYERED: { /* Layered Action. */
429 /* This assumes that the ALE_ACTION_LAYERED channel is shown in the dope-sheet context,
430 * underneath the data-block that owns `ale->adt`. So that means that the loop is limited to
431 * the keys that belong to that slot. */
432 animrig::Action &action = static_cast<bAction *>(ale->key_data)->wrap();
433 animrig::Slot *slot = action.slot_for_handle(ale->adt->slot_handle);
434 return action_layered_keyframes_loop(ked, action, slot, key_ok, key_cb, fcu_cb);
435 }
436 case ALE_ACTION_SLOT: {
437 animrig::Action *action = static_cast<animrig::Action *>(ale->key_data);
438 BLI_assert(action);
439 animrig::Slot *slot = static_cast<animrig::Slot *>(ale->data);
440 return action_layered_keyframes_loop(ked, *action, slot, key_ok, key_cb, fcu_cb);
441 }
442
443 case ALE_ACT: /* Legacy Action. */
445 ked, static_cast<bAction *>(ale->key_data), key_ok, key_cb, fcu_cb);
446 case ALE_OB: /* object */
447 return ob_keyframes_loop(
448 ked, ads, static_cast<Object *>(ale->key_data), key_ok, key_cb, fcu_cb);
449 case ALE_SCE: /* scene */
451 ked, ads, static_cast<Scene *>(ale->data), key_ok, key_cb, fcu_cb);
452 case ALE_ALL: /* 'all' (DopeSheet summary) */
454 ked, static_cast<bAnimContext *>(ale->data), key_ok, key_cb, fcu_cb);
455
456 case ALE_NONE:
457 case ALE_GPFRAME:
458 case ALE_MASKLAY:
459 case ALE_NLASTRIP:
463 break;
464 }
465
466 return 0;
467}
468
471 KeyframeEditFunc callback_fn)
472{
473 ListBase anim_data = {nullptr, nullptr};
474
476 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
477
478 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
480 static_cast<FCurve *>(ale->key_data),
481 nullptr,
482 callback_fn,
484 ale->update |= ANIM_UPDATE_DEFAULT;
485 }
486
487 ANIM_animdata_update(ac, &anim_data);
488 ANIM_animdata_freelist(&anim_data);
489}
490
491/* ************************************************************************** */
492/* Keyframe Integrity Tools */
493
495{
496 ListBase anim_data = {nullptr, nullptr};
497 int filter;
498
499 /* filter animation data */
502 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
503
504 /* Loop over F-Curves that are likely to have been edited, and tag them to
505 * ensure the keyframes are in order and handles are in a valid position. */
506 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
508 }
509
510 /* free temp data */
511 ANIM_animdata_update(ac, &anim_data);
512 ANIM_animdata_freelist(&anim_data);
513}
514
515/* ************************************************************************** */
516/* BezTriple Validation Callbacks */
517
518/* ------------------------ */
519
521{
522 const bool handles_shown = (ked->iterflags & KEYFRAME_ITER_HANDLES_INVISIBLE) == 0;
523 if (!handles_shown) {
524 return false;
525 }
526 const bool handles_shown_only_selected = ked->iterflags &
528
529 return handles_shown_only_selected ? BEZT_ISSEL_ANY(bezt) : true;
530}
531
533 KeyframeEditData *ked,
534 BezTriple *bezt,
535 blender::FunctionRef<bool(KeyframeEditData *ked, BezTriple *bezt, const int index)> check)
536{
537 short ok = 0;
538 if (check(ked, bezt, 1)) {
539 ok |= KEYFRAME_OK_KEY;
540 }
541 if (ked && (ked->iterflags & KEYFRAME_ITER_INCL_HANDLES))
542 { /* Only act on visible items, so check handle visibility state. */
543 if (handles_visible(ked, bezt)) {
544 if (check(ked, bezt, 0)) {
545 ok |= KEYFRAME_OK_H1;
546 }
547 if (check(ked, bezt, 2)) {
548 ok |= KEYFRAME_OK_H2;
549 }
550 }
551 }
552 return ok;
553}
554
555/* ------------------------ */
556
558{
559 /* frame is stored in f1 property (this float accuracy check may need to be dropped?) */
560 const short ok = keyframe_ok_checks(
561 ked, bezt, [](KeyframeEditData *ked, BezTriple *bezt, int index) -> bool {
562 return IS_EQF(bezt->vec[index][0], ked->f1);
563 });
564
565 return ok;
566}
567
569{
570 const short ok = keyframe_ok_checks(
571 ked, bezt, [](KeyframeEditData *ked, BezTriple *bezt, int index) -> bool {
572 return (bezt->vec[index][0] > ked->f1) && (bezt->vec[index][0] < ked->f2);
573 });
574
575 return ok;
576}
577
578static short ok_bezier_selected(KeyframeEditData * /*ked*/, BezTriple *bezt)
579{
580 /* this macro checks all beztriple handles for selection...
581 * only one of the verts has to be selected for this to be ok...
582 */
583 if (BEZT_ISSEL_ANY(bezt)) {
584 return KEYFRAME_OK_ALL;
585 }
586 return 0;
587}
588
590{
591 /* This macro checks the beztriple key (f2) selection. */
592 if (BEZT_ISSEL_IDX(bezt, 1)) {
593 return KEYFRAME_OK_KEY;
594 }
595 return 0;
596}
597
599{
600 /* Value is stored in f1 property:
601 * - This float accuracy check may need to be dropped?
602 * - Should value be stored in f2 instead
603 * so that we won't have conflicts when using f1 for frames too?
604 */
605 const short ok = keyframe_ok_checks(
606 ked, bezt, [](KeyframeEditData *ked, BezTriple *bezt, int index) -> bool {
607 return IS_EQF(bezt->vec[index][1], ked->f1);
608 });
609
610 return ok;
611}
612
614{
615 /* value range is stored in float properties */
616 const short ok = keyframe_ok_checks(
617 ked, bezt, [](KeyframeEditData *ked, BezTriple *bezt, int index) -> bool {
618 return (bezt->vec[index][1] > ked->f1) && (bezt->vec[index][1] < ked->f2);
619 });
620
621 return ok;
622}
623
625{
626 /* rect is stored in data property (it's of type rectf, but may not be set) */
627 if (!ked->data) {
628 return 0;
629 }
630
631 const short ok = keyframe_ok_checks(
632 ked, bezt, [](KeyframeEditData *ked, BezTriple *bezt, int index) -> bool {
633 return BLI_rctf_isect_pt_v(static_cast<rctf *>(ked->data), bezt->vec[index]);
634 });
635
636 return ok;
637}
638
639bool keyframe_region_lasso_test(const KeyframeEdit_LassoData *data_lasso, const float xy[2])
640{
641 if (BLI_rctf_isect_pt_v(data_lasso->rectf_scaled, xy)) {
642 float xy_view[2];
643
644 BLI_rctf_transform_pt_v(data_lasso->rectf_view, data_lasso->rectf_scaled, xy_view, xy);
645
646 if (BLI_lasso_is_point_inside(data_lasso->mcoords, xy_view[0], xy_view[1], INT_MAX)) {
647 return true;
648 }
649 }
650
651 return false;
652}
653
655{
656 /* check for lasso customdata (KeyframeEdit_LassoData) */
657 if (!ked->data) {
658 return 0;
659 }
660
661 const short ok = keyframe_ok_checks(
662 ked, bezt, [](KeyframeEditData *ked, BezTriple *bezt, int index) -> bool {
663 return keyframe_region_lasso_test(static_cast<KeyframeEdit_LassoData *>(ked->data),
664 bezt->vec[index]);
665 });
666
667 return ok;
668}
669
671{
672 /* check for lasso customdata (KeyframeEdit_LassoData) */
673 if (ked->data) {
675 float pt[2];
676
677 /* late-binding remap of the x values (for summary channels) */
678 /* XXX: Ideally we reset, but it should be fine just leaving it as-is
679 * as the next channel will reset it properly, while the next summary-channel
680 * curve will also reset by itself...
681 */
683 data->rectf_scaled->xmin = ked->f1;
684 data->rectf_scaled->xmax = ked->f2;
685 }
686
687 /* only use the x-coordinate of the point; the y is the channel range... */
688 pt[0] = bezt->vec[1][0];
689 pt[1] = ked->channel_y;
690
692 return KEYFRAME_OK_KEY;
693 }
694 }
695 return 0;
696}
697
698bool keyframe_region_circle_test(const KeyframeEdit_CircleData *data_circle, const float xy[2])
699{
700 if (BLI_rctf_isect_pt_v(data_circle->rectf_scaled, xy)) {
701 float xy_view[2];
702
703 BLI_rctf_transform_pt_v(data_circle->rectf_view, data_circle->rectf_scaled, xy_view, xy);
704
705 xy_view[0] = xy_view[0] - data_circle->mval[0];
706 xy_view[1] = xy_view[1] - data_circle->mval[1];
707 return len_squared_v2(xy_view) < data_circle->radius_squared;
708 }
709
710 return false;
711}
712
714{
715 /* check for circle select customdata (KeyframeEdit_CircleData) */
716 if (!ked->data) {
717 return 0;
718 }
719
720 const short ok = keyframe_ok_checks(
721 ked, bezt, [](KeyframeEditData *ked, BezTriple *bezt, int index) -> bool {
722 return keyframe_region_circle_test(static_cast<KeyframeEdit_CircleData *>(ked->data),
723 bezt->vec[index]);
724 });
725
726 return ok;
727}
728
730{
731 /* check for circle select customdata (KeyframeEdit_CircleData) */
732 if (ked->data) {
734 float pt[2];
735
736 /* late-binding remap of the x values (for summary channels) */
737 /* XXX: Ideally we reset, but it should be fine just leaving it as-is
738 * as the next channel will reset it properly, while the next summary-channel
739 * curve will also reset by itself...
740 */
742 data->rectf_scaled->xmin = ked->f1;
743 data->rectf_scaled->xmax = ked->f2;
744 }
745
746 /* only use the x-coordinate of the point; the y is the channel range... */
747 pt[0] = bezt->vec[1][0];
748 pt[1] = ked->channel_y;
749
751 return KEYFRAME_OK_KEY;
752 }
753 }
754 return 0;
755}
756
758{
759 /* eEditKeyframes_Validate */
760 switch (mode) {
761 case BEZT_OK_FRAME:
762 /* only if bezt falls on the right frame (float) */
763 return ok_bezier_frame;
765 /* only if bezt falls within the specified frame range (floats) */
767 case BEZT_OK_SELECTED:
768 /* only if bezt is selected (any of f1, f2, f3) */
769 return ok_bezier_selected;
771 /* only if bezt is selected (f2 is enough) */
773 case BEZT_OK_VALUE:
774 /* only if bezt value matches (float) */
775 return ok_bezier_value;
777 /* only if bezier falls within the specified value range (floats) */
779 case BEZT_OK_REGION:
780 /* only if bezier falls within the specified rect (data -> rectf) */
781 return ok_bezier_region;
783 /* only if the point falls within KeyframeEdit_LassoData defined data */
786 /* only if the point falls within KeyframeEdit_CircleData defined data */
789 /* same as BEZT_OK_REGION_LASSO, but we're only using the x-value of the points */
792 /* same as BEZT_OK_REGION_CIRCLE, but we're only using the x-value of the points */
794 default: /* nothing was ok */
795 return nullptr;
796 }
797}
798
799/* ******************************************* */
800/* Assorted Utility Functions */
801
803{
804 /* only if selected */
805 if (bezt->f2 & SELECT) {
806 /* store average time in float 1 (only do rounding at last step) */
807 ked->f1 += bezt->vec[1][0];
808
809 /* store average value in float 2 (only do rounding at last step)
810 * - this isn't always needed, but some operators may also require this
811 */
812 ked->f2 += bezt->vec[1][1];
813
814 /* increment number of items */
815 ked->i1++;
816 }
817
818 return 0;
819}
820
822{
823 /* only if selected */
824 if ((bezt->f2 & SELECT) == 0) {
825 return 0;
826 }
827
828 CfraElem *ce = MEM_callocN<CfraElem>("cfraElem");
829 BLI_addtail(&ked->list, ce);
830
831 /* bAnimListElem so we can do NLA mapping, we want the cfra to be in "global" time */
832 bAnimListElem *ale = static_cast<bAnimListElem *>(ked->data);
833 if (ale != nullptr) {
834 ce->cfra = ANIM_nla_tweakedit_remap(ale, bezt->vec[1][0], NLATIME_CONVERT_MAP);
835 }
836 else {
837 ce->cfra = bezt->vec[1][0];
838 }
839
840 return 0;
841}
842
844{
845 KeyframeEditCD_Remap *rmap = static_cast<KeyframeEditCD_Remap *>(ked->data);
846 const float scale = (rmap->newMax - rmap->newMin) / (rmap->oldMax - rmap->oldMin);
847
848 /* perform transform on all three handles unless indicated otherwise */
849 /* TODO: need to include some checks for that */
850
851 bezt->vec[0][0] = scale * (bezt->vec[0][0] - rmap->oldMin) + rmap->newMin;
852 bezt->vec[1][0] = scale * (bezt->vec[1][0] - rmap->oldMin) + rmap->newMin;
853 bezt->vec[2][0] = scale * (bezt->vec[2][0] - rmap->oldMin) + rmap->newMin;
854}
855
856/* ******************************************* */
857/* Transform */
858
859/* snaps the keyframe to the nearest frame */
860static short snap_bezier_nearest(KeyframeEditData * /*ked*/, BezTriple *bezt)
861{
862 if (bezt->f2 & SELECT) {
863 BKE_fcurve_keyframe_move_time_with_handles(bezt, floorf(bezt->vec[1][0] + 0.5f));
864 }
865 return 0;
866}
867
868/* snaps the keyframe to the nearest second */
870{
871 const Scene *scene = ked->scene;
872 const float secf = float(FPS);
873
874 if (bezt->f2 & SELECT) {
875 BKE_fcurve_keyframe_move_time_with_handles(bezt, floorf(bezt->vec[1][0] / secf + 0.5f) * secf);
876 }
877 return 0;
878}
879
880/* snaps the keyframe to the current frame */
882{
883 const Scene *scene = ked->scene;
884 if (bezt->f2 & SELECT) {
886 }
887 return 0;
888}
889
890/* snaps the keyframe time to the nearest marker's frame */
892{
893 if (bezt->f2 & SELECT) {
895 bezt, float(ED_markers_find_nearest_marker_time(&ked->list, bezt->vec[1][0])));
896 }
897 return 0;
898}
899
900/* make the handles have the same value as the key */
902{
903 if (bezt->f2 & SELECT) {
904 bezt->vec[0][1] = bezt->vec[2][1] = bezt->vec[1][1];
905
906 if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) {
907 bezt->h1 = HD_ALIGN;
908 }
909 if (ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) {
910 bezt->h2 = HD_ALIGN;
911 }
912 }
913 return 0;
914}
915
916/* frame to snap to is stored in the custom data -> first float value slot */
918{
919 if (bezt->f2 & SELECT) {
921 }
922 return 0;
923}
924
925/* value to snap to is stored in the custom data -> first float value slot */
927{
928 if (bezt->f2 & SELECT) {
930 }
931 return 0;
932}
933
935{
936 /* eEditKeyframes_Snap */
937 switch (mode) {
938 case SNAP_KEYS_NEARFRAME: /* snap to nearest frame */
939 return snap_bezier_nearest;
940 case SNAP_KEYS_CURFRAME: /* snap to current frame */
941 return snap_bezier_cframe;
942 case SNAP_KEYS_NEARMARKER: /* snap to nearest marker */
944 case SNAP_KEYS_NEARSEC: /* snap to nearest second */
946 case SNAP_KEYS_HORIZONTAL: /* snap handles to same value */
948 case SNAP_KEYS_TIME: /* snap to given frame/time */
949 return snap_bezier_time;
950 case SNAP_KEYS_VALUE: /* snap to given value */
951 return snap_bezier_value;
952 default: /* just in case */
953 return snap_bezier_nearest;
954 }
955}
956
957/* --------- */
958
959static void mirror_bezier_xaxis_ex(BezTriple *bezt, const float center)
960{
961 for (int i = 0; i < 3; i++) {
962 float diff = (center - bezt->vec[i][0]);
963 bezt->vec[i][0] = (center + diff);
964 }
965 swap_v3_v3(bezt->vec[0], bezt->vec[2]);
966
967 std::swap(bezt->h1, bezt->h2);
968 std::swap(bezt->f1, bezt->f3);
969}
970
971static void mirror_bezier_yaxis_ex(BezTriple *bezt, const float center)
972{
973 for (int i = 0; i < 3; i++) {
974 float diff = (center - bezt->vec[i][1]);
975 bezt->vec[i][1] = (center + diff);
976 }
977}
978
980{
981 const Scene *scene = ked->scene;
982
983 if (bezt->f2 & SELECT) {
984 mirror_bezier_xaxis_ex(bezt, scene->r.cfra);
985 }
986
987 return 0;
988}
989
990static short mirror_bezier_yaxis(KeyframeEditData * /*ked*/, BezTriple *bezt)
991{
992 if (bezt->f2 & SELECT) {
993 /* Yes, names are inverted, we are mirroring across y axis, hence along x axis... */
994 mirror_bezier_xaxis_ex(bezt, 0.0f);
995 }
996
997 return 0;
998}
999
1000static short mirror_bezier_xaxis(KeyframeEditData * /*ked*/, BezTriple *bezt)
1001{
1002 if (bezt->f2 & SELECT) {
1003 /* Yes, names are inverted, we are mirroring across x axis, hence along y axis... */
1004 mirror_bezier_yaxis_ex(bezt, 0.0f);
1005 }
1006
1007 return 0;
1008}
1009
1011{
1012 /* mirroring time stored in f1 */
1013 if (bezt->f2 & SELECT) {
1014 mirror_bezier_xaxis_ex(bezt, ked->f1);
1015 }
1016
1017 return 0;
1018}
1019
1021{
1022 /* value to mirror over is stored in f1 */
1023 if (bezt->f2 & SELECT) {
1024 mirror_bezier_xaxis_ex(bezt, ked->f1);
1025 }
1026
1027 return 0;
1028}
1029
1031{
1032 /* value to mirror over is stored in the custom data -> first float value slot */
1033 if (bezt->f2 & SELECT) {
1034 mirror_bezier_yaxis_ex(bezt, ked->f1);
1035 }
1036
1037 return 0;
1038}
1039
1041{
1042 switch (mode) {
1043 case MIRROR_KEYS_CURFRAME: /* mirror over current frame */
1044 return mirror_bezier_cframe;
1045 case MIRROR_KEYS_YAXIS: /* mirror over frame 0 */
1046 return mirror_bezier_yaxis;
1047 case MIRROR_KEYS_XAXIS: /* mirror over value 0 */
1048 return mirror_bezier_xaxis;
1049 case MIRROR_KEYS_MARKER: /* mirror over marker */
1050 return mirror_bezier_marker;
1051 case MIRROR_KEYS_TIME: /* mirror over frame/time */
1052 return mirror_bezier_time;
1053 case MIRROR_KEYS_VALUE: /* mirror over given value */
1054 return mirror_bezier_value;
1055 default: /* just in case */
1056 return mirror_bezier_yaxis;
1057 }
1058}
1059
1060/* ******************************************* */
1061/* Settings */
1062
1068#define ENSURE_HANDLES_MATCH(bezt) \
1069 if (bezt->h1 != bezt->h2) { \
1070 if (ELEM(bezt->h1, HD_ALIGN, HD_AUTO, HD_AUTO_ANIM)) { \
1071 bezt->h1 = HD_FREE; \
1072 } \
1073 if (ELEM(bezt->h2, HD_ALIGN, HD_AUTO, HD_AUTO_ANIM)) { \
1074 bezt->h2 = HD_FREE; \
1075 } \
1076 } \
1077 (void)0
1078
1079/* Sets the selected bezier handles to type 'auto' */
1080static short set_bezier_auto(KeyframeEditData * /*ked*/, BezTriple *bezt)
1081{
1082 /* If the key is selected, always apply to both handles. */
1083 if (bezt->f2 & SELECT) {
1084 bezt->h1 = bezt->h2 = HD_AUTO;
1085 }
1086 else {
1087 if (bezt->f1 & SELECT) {
1088 bezt->h1 = HD_AUTO;
1089 }
1090 if (bezt->f3 & SELECT) {
1091 bezt->h2 = HD_AUTO;
1092 }
1093
1095 }
1096
1097 return 0;
1098}
1099
1100/* Sets the selected bezier handles to type 'auto-clamped'
1101 * NOTE: this is like auto above, but they're handled a bit different
1102 */
1104{
1105 /* If the key is selected, always apply to both handles. */
1106 if (bezt->f2 & SELECT) {
1107 bezt->h1 = bezt->h2 = HD_AUTO_ANIM;
1108 }
1109 else {
1110 if (bezt->f1 & SELECT) {
1111 bezt->h1 = HD_AUTO_ANIM;
1112 }
1113 if (bezt->f3 & SELECT) {
1114 bezt->h2 = HD_AUTO_ANIM;
1115 }
1116
1118 }
1119
1120 return 0;
1121}
1122
1123/* Sets the selected bezier handles to type 'vector'. */
1124static short set_bezier_vector(KeyframeEditData * /*ked*/, BezTriple *bezt)
1125{
1126 /* If the key is selected, always apply to both handles. */
1127 if (bezt->f2 & SELECT) {
1128 bezt->h1 = bezt->h2 = HD_VECT;
1129 }
1130 else {
1131 if (bezt->f1 & SELECT) {
1132 bezt->h1 = HD_VECT;
1133 }
1134 if (bezt->f3 & SELECT) {
1135 bezt->h2 = HD_VECT;
1136 }
1137 }
1138
1139 return 0;
1140}
1141
1148static short bezier_isfree(KeyframeEditData * /*ked*/, BezTriple *bezt)
1149{
1150 if ((bezt->f1 & SELECT) && (bezt->h1)) {
1151 return 1;
1152 }
1153 if ((bezt->f3 & SELECT) && (bezt->h2)) {
1154 return 1;
1155 }
1156 return 0;
1157}
1158
1159/* Sets selected bezier handles to type 'align' */
1160static short set_bezier_align(KeyframeEditData * /*ked*/, BezTriple *bezt)
1161{
1162 /* If the key is selected, always apply to both handles. */
1163 if (bezt->f2 & SELECT) {
1164 bezt->h1 = bezt->h2 = HD_ALIGN;
1165 }
1166 else {
1167 if (bezt->f1 & SELECT) {
1168 bezt->h1 = HD_ALIGN;
1169 }
1170 if (bezt->f3 & SELECT) {
1171 bezt->h2 = HD_ALIGN;
1172 }
1173 }
1174
1175 return 0;
1176}
1177
1178/* Sets selected bezier handles to type 'free'. */
1179static short set_bezier_free(KeyframeEditData * /*ked*/, BezTriple *bezt)
1180{
1181 /* If the key is selected, always apply to both handles. */
1182 if (bezt->f2 & SELECT) {
1183 bezt->h1 = bezt->h2 = HD_FREE;
1184 }
1185 else {
1186 if (bezt->f1 & SELECT) {
1187 bezt->h1 = HD_FREE;
1188 }
1189 if (bezt->f3 & SELECT) {
1190 bezt->h2 = HD_FREE;
1191 }
1192 }
1193
1194 return 0;
1195}
1196
1198{
1199 switch (mode) {
1200 case HD_AUTO: /* auto */
1201 return set_bezier_auto;
1202 case HD_AUTO_ANIM: /* auto clamped */
1204
1205 case HD_VECT: /* vector */
1206 return set_bezier_vector;
1207 case HD_FREE: /* free */
1208 return set_bezier_free;
1209 case HD_ALIGN: /* align */
1210 return set_bezier_align;
1211
1212 default: /* check for toggle free or align? */
1213 return bezier_isfree;
1214 }
1215}
1216
1217/* ------- */
1218
1219static short set_bezt_constant(KeyframeEditData * /*ked*/, BezTriple *bezt)
1220{
1221 if (bezt->f2 & SELECT) {
1222 bezt->ipo = BEZT_IPO_CONST;
1223 }
1224 return 0;
1225}
1226
1227static short set_bezt_linear(KeyframeEditData * /*ked*/, BezTriple *bezt)
1228{
1229 if (bezt->f2 & SELECT) {
1230 bezt->ipo = BEZT_IPO_LIN;
1231 }
1232 return 0;
1233}
1234
1235static short set_bezt_bezier(KeyframeEditData * /*ked*/, BezTriple *bezt)
1236{
1237 if (bezt->f2 & SELECT) {
1238 bezt->ipo = BEZT_IPO_BEZ;
1239 }
1240 return 0;
1241}
1242
1243static short set_bezt_back(KeyframeEditData * /*ked*/, BezTriple *bezt)
1244{
1245 if (bezt->f2 & SELECT) {
1246 bezt->ipo = BEZT_IPO_BACK;
1247 }
1248 return 0;
1249}
1250
1251static short set_bezt_bounce(KeyframeEditData * /*ked*/, BezTriple *bezt)
1252{
1253 if (bezt->f2 & SELECT) {
1254 bezt->ipo = BEZT_IPO_BOUNCE;
1255 }
1256 return 0;
1257}
1258
1259static short set_bezt_circle(KeyframeEditData * /*ked*/, BezTriple *bezt)
1260{
1261 if (bezt->f2 & SELECT) {
1262 bezt->ipo = BEZT_IPO_CIRC;
1263 }
1264 return 0;
1265}
1266
1267static short set_bezt_cubic(KeyframeEditData * /*ked*/, BezTriple *bezt)
1268{
1269 if (bezt->f2 & SELECT) {
1270 bezt->ipo = BEZT_IPO_CUBIC;
1271 }
1272 return 0;
1273}
1274
1275static short set_bezt_elastic(KeyframeEditData * /*ked*/, BezTriple *bezt)
1276{
1277 if (bezt->f2 & SELECT) {
1278 bezt->ipo = BEZT_IPO_ELASTIC;
1279 }
1280 return 0;
1281}
1282
1283static short set_bezt_expo(KeyframeEditData * /*ked*/, BezTriple *bezt)
1284{
1285 if (bezt->f2 & SELECT) {
1286 bezt->ipo = BEZT_IPO_EXPO;
1287 }
1288 return 0;
1289}
1290
1291static short set_bezt_quad(KeyframeEditData * /*ked*/, BezTriple *bezt)
1292{
1293 if (bezt->f2 & SELECT) {
1294 bezt->ipo = BEZT_IPO_QUAD;
1295 }
1296 return 0;
1297}
1298
1299static short set_bezt_quart(KeyframeEditData * /*ked*/, BezTriple *bezt)
1300{
1301 if (bezt->f2 & SELECT) {
1302 bezt->ipo = BEZT_IPO_QUART;
1303 }
1304 return 0;
1305}
1306
1307static short set_bezt_quint(KeyframeEditData * /*ked*/, BezTriple *bezt)
1308{
1309 if (bezt->f2 & SELECT) {
1310 bezt->ipo = BEZT_IPO_QUINT;
1311 }
1312 return 0;
1313}
1314
1315static short set_bezt_sine(KeyframeEditData * /*ked*/, BezTriple *bezt)
1316{
1317 if (bezt->f2 & SELECT) {
1318 bezt->ipo = BEZT_IPO_SINE;
1319 }
1320 return 0;
1321}
1322
1323static void handle_flatten(float vec[3][3], const int idx, const float direction[2])
1324{
1325 BLI_assert_msg(idx == 0 || idx == 2, "handle_flatten() expects a handle index");
1326
1327 add_v2_v2v2(vec[idx], vec[1], direction);
1328}
1329
1330static void handle_set_length(float vec[3][3], const int idx, const float handle_length)
1331{
1332 BLI_assert_msg(idx == 0 || idx == 2, "handle_set_length() expects a handle index");
1333
1334 float handle_direction[2];
1335 sub_v2_v2v2(handle_direction, vec[idx], vec[1]);
1336 normalize_v2_length(handle_direction, handle_length);
1337 add_v2_v2v2(vec[idx], vec[1], handle_direction);
1338}
1339
1341 const eEditKeyframes_Equalize mode,
1342 const float handle_length,
1343 const bool flatten)
1344{
1345 uint i;
1346 BezTriple *bezt;
1347 const float flat_direction_left[2] = {-handle_length, 0.0f};
1348 const float flat_direction_right[2] = {handle_length, 0.0f};
1349
1350 /* Loop through an F-Curves keyframes. */
1351 for (bezt = fcu->bezt, i = 0; i < fcu->totvert; bezt++, i++) {
1352 if ((bezt->f2 & SELECT) == 0) {
1353 continue;
1354 }
1355
1356 /* Perform handle equalization if mode is 'Both' or 'Left'. */
1357 if (mode & EQUALIZE_HANDLES_LEFT) {
1358 /* If left handle type is 'Auto', 'Auto Clamped', or 'Vector', convert handles to
1359 * 'Aligned'.
1360 */
1361 if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) {
1362 bezt->h1 = HD_ALIGN;
1363 bezt->h2 = HD_ALIGN;
1364 }
1365
1366 if (flatten) {
1367 handle_flatten(bezt->vec, 0, flat_direction_left);
1368 }
1369 else {
1370 handle_set_length(bezt->vec, 0, handle_length);
1371 }
1372 }
1373
1374 /* Perform handle equalization if mode is 'Both' or 'Right'. */
1375 if (mode & EQUALIZE_HANDLES_RIGHT) {
1376 /* If right handle type is 'Auto', 'Auto Clamped', or 'Vector', convert handles to
1377 * 'Aligned'. */
1378 if (ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) {
1379 bezt->h1 = HD_ALIGN;
1380 bezt->h2 = HD_ALIGN;
1381 }
1382
1383 if (flatten) {
1384 handle_flatten(bezt->vec, 2, flat_direction_right);
1385 }
1386 else {
1387 handle_set_length(bezt->vec, 2, handle_length);
1388 }
1389 }
1390 }
1391}
1392
1394{
1395 switch (mode) {
1396 /* interpolation */
1397 case BEZT_IPO_CONST: /* constant */
1398 return set_bezt_constant;
1399 case BEZT_IPO_LIN: /* linear */
1400 return set_bezt_linear;
1401
1402 /* easing */
1403 case BEZT_IPO_BACK:
1404 return set_bezt_back;
1405 case BEZT_IPO_BOUNCE:
1406 return set_bezt_bounce;
1407 case BEZT_IPO_CIRC:
1408 return set_bezt_circle;
1409 case BEZT_IPO_CUBIC:
1410 return set_bezt_cubic;
1411 case BEZT_IPO_ELASTIC:
1412 return set_bezt_elastic;
1413 case BEZT_IPO_EXPO:
1414 return set_bezt_expo;
1415 case BEZT_IPO_QUAD:
1416 return set_bezt_quad;
1417 case BEZT_IPO_QUART:
1418 return set_bezt_quart;
1419 case BEZT_IPO_QUINT:
1420 return set_bezt_quint;
1421 case BEZT_IPO_SINE:
1422 return set_bezt_sine;
1423
1424 default: /* bezier */
1425 return set_bezt_bezier;
1426 }
1427}
1428
1429/* ------- */
1430
1432{
1433 if (bezt->f2 & SELECT) {
1435 }
1436 return 0;
1437}
1438
1440{
1441 if (bezt->f2 & SELECT) {
1443 }
1444 return 0;
1445}
1446
1447static short set_keytype_extreme(KeyframeEditData * /*ked*/, BezTriple *bezt)
1448{
1449 if (bezt->f2 & SELECT) {
1451 }
1452 return 0;
1453}
1454
1455static short set_keytype_jitter(KeyframeEditData * /*ked*/, BezTriple *bezt)
1456{
1457 if (bezt->f2 & SELECT) {
1459 }
1460 return 0;
1461}
1462
1464{
1465 if (bezt->f2 & SELECT) {
1467 }
1468 return 0;
1469}
1470
1472{
1473 if (bezt->f2 & SELECT) {
1475 }
1476 return 0;
1477}
1478
1480{
1481 switch (keyframe_type) {
1483 return set_keytype_breakdown;
1484
1486 return set_keytype_extreme;
1487
1489 return set_keytype_jitter;
1490
1493
1495 return set_keytype_keyframe;
1496
1498 return set_keytype_generated;
1499 }
1500
1502 return nullptr;
1503}
1504
1505/* ------- */
1506
1508{
1509 if (bezt->f2 & SELECT) {
1510 bezt->easing = BEZT_IPO_EASE_IN;
1511 }
1512 return 0;
1513}
1514
1516{
1517 if (bezt->f2 & SELECT) {
1518 bezt->easing = BEZT_IPO_EASE_OUT;
1519 }
1520 return 0;
1521}
1522
1524{
1525 if (bezt->f2 & SELECT) {
1527 }
1528 return 0;
1529}
1530
1532{
1533 if (bezt->f2 & SELECT) {
1534 bezt->easing = BEZT_IPO_EASE_AUTO;
1535 }
1536 return 0;
1537}
1538
1540{
1541 switch (mode) {
1542 case BEZT_IPO_EASE_IN: /* ease in */
1543 return set_easingtype_easein;
1544
1545 case BEZT_IPO_EASE_OUT: /* ease out */
1547
1548 case BEZT_IPO_EASE_IN_OUT: /* both */
1550
1551 default: /* auto */
1553 }
1554}
1555
1556/* ******************************************* */
1557/* Selection */
1558
1560{
1561 /* Only act on visible items, so check handle visibility state. */
1562 /* if we've got info on what to select, use it, otherwise select all */
1563 if ((ked) && (ked->iterflags & KEYFRAME_ITER_INCL_HANDLES) && handles_visible(ked, bezt)) {
1564 if (ked->curflags & KEYFRAME_OK_KEY) {
1565 bezt->f2 |= SELECT;
1566 }
1567 if (ked->curflags & KEYFRAME_OK_H1) {
1568 bezt->f1 |= SELECT;
1569 }
1570 if (ked->curflags & KEYFRAME_OK_H2) {
1571 bezt->f3 |= SELECT;
1572 }
1573 }
1574 else {
1575 BEZT_SEL_ALL(bezt);
1576 }
1577
1578 return 0;
1579}
1580
1582{
1583 /* Only act on visible items, so check handle visibility state. */
1584 /* if we've got info on what to deselect, use it, otherwise deselect all */
1585 if ((ked) && (ked->iterflags & KEYFRAME_ITER_INCL_HANDLES) && handles_visible(ked, bezt)) {
1586 if (ked->curflags & KEYFRAME_OK_KEY) {
1587 bezt->f2 &= ~SELECT;
1588 }
1589 if (ked->curflags & KEYFRAME_OK_H1) {
1590 bezt->f1 &= ~SELECT;
1591 }
1592 if (ked->curflags & KEYFRAME_OK_H2) {
1593 bezt->f3 &= ~SELECT;
1594 }
1595 }
1596 else {
1597 BEZT_DESEL_ALL(bezt);
1598 }
1599
1600 return 0;
1601}
1602
1604{
1605 /* Invert the selection for the whole bezier triple */
1606 bezt->f2 ^= SELECT;
1607 if (bezt->f2 & SELECT) {
1608 bezt->f1 |= SELECT;
1609 bezt->f3 |= SELECT;
1610 }
1611 else {
1612 bezt->f1 &= ~SELECT;
1613 bezt->f3 &= ~SELECT;
1614 }
1615 return 0;
1616}
1617
1619{
1620 switch (selectmode) {
1621 case SELECT_ADD: /* add */
1622 return select_bezier_add;
1623 case SELECT_SUBTRACT: /* subtract */
1625 case SELECT_INVERT: /* invert */
1626 return select_bezier_invert;
1627 default: /* replace (need to clear all, then add) */
1628 return select_bezier_add;
1629 }
1630}
1631
1632/* ******************************************* */
1633/* Selection Maps */
1634
1635/* Selection maps are simply fancy names for char arrays that store on/off
1636 * info for whether the selection status. The main purpose for these is to
1637 * allow extra info to be tagged to the keyframes without influencing their
1638 * values or having to be removed later.
1639 */
1640
1641/* ----------- */
1642
1644{
1645 const FCurve *fcu = ked->fcu;
1646 char *map = static_cast<char *>(ked->data);
1647 int i = ked->curIndex;
1648
1649 /* if current is selected, just make sure it stays this way */
1650 if (BEZT_ISSEL_ANY(bezt)) {
1651 map[i] = 1;
1652 return 0;
1653 }
1654
1655 /* if previous is selected, that means that selection should extend across */
1656 if (i > 0) {
1657 BezTriple *prev = bezt - 1;
1658
1659 if (BEZT_ISSEL_ANY(prev)) {
1660 map[i] = 1;
1661 return 0;
1662 }
1663 }
1664
1665 /* if next is selected, that means that selection should extend across */
1666 if (i < (fcu->totvert - 1)) {
1667 BezTriple *next = bezt + 1;
1668
1669 if (BEZT_ISSEL_ANY(next)) {
1670 map[i] = 1;
1671 return 0;
1672 }
1673 }
1674
1675 return 0;
1676}
1677
1679{
1680 const FCurve *fcu = ked->fcu;
1681 char *map = static_cast<char *>(ked->data);
1682 int i = ked->curIndex;
1683
1684 /* if current is selected, check the left/right keyframes
1685 * since it might need to be deselected (but otherwise no)
1686 */
1687 if (BEZT_ISSEL_ANY(bezt)) {
1688 /* if previous is not selected, we're on the tip of an iceberg */
1689 if (i > 0) {
1690 BezTriple *prev = bezt - 1;
1691
1692 if (BEZT_ISSEL_ANY(prev) == 0) {
1693 return 0;
1694 }
1695 }
1696 else if (i == 0) {
1697 /* current keyframe is selected at an endpoint, so should get deselected */
1698 return 0;
1699 }
1700
1701 /* if next is not selected, we're on the tip of an iceberg */
1702 if (i < (fcu->totvert - 1)) {
1703 BezTriple *next = bezt + 1;
1704
1705 if (BEZT_ISSEL_ANY(next) == 0) {
1706 return 0;
1707 }
1708 }
1709 else if (i == (fcu->totvert - 1)) {
1710 /* current keyframe is selected at an endpoint, so should get deselected */
1711 return 0;
1712 }
1713
1714 /* if we're still here, that means that keyframe should remain untouched */
1715 map[i] = 1;
1716 }
1717
1718 return 0;
1719}
1720
1722{
1723 switch (mode) {
1724 case SELMAP_LESS: /* less */
1726
1727 case SELMAP_MORE: /* more */
1728 default:
1730 }
1731}
1732
1733/* ----------- */
1734
1736{
1737 const char *map = static_cast<char *>(ked->data);
1738 short on = map[ked->curIndex];
1739
1740 /* select or deselect based on whether the map allows it or not */
1741 if (on) {
1742 BEZT_SEL_ALL(bezt);
1743 }
1744 else {
1745 BEZT_DESEL_ALL(bezt);
1746 }
1747
1748 return 0;
1749}
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:543
@ NLATIME_CONVERT_UNMAP
Definition BKE_nla.hh:540
#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)
@ 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.
#define FPS
@ 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:262
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
#define SELECT
#define floorf(x)
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:71
return ret
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
eAnimCont_Types datatype
bDopeSheet * ads
AnimData * adt
eAnim_ChannelType type
eAnim_KeyType datatype
i
Definition text_draw.cc:230
int xy[2]
Definition wm_draw.cc:174