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
470 bDopeSheet *ads,
471 void *data,
472 int keytype,
473 KeyframeEditFunc key_ok,
474 KeyframeEditFunc key_cb,
475 FcuEditFunc fcu_cb)
476{
477 /* sanity checks */
478 if (data == nullptr) {
479 return 0;
480 }
481
482 /* method to use depends on the type of keyframe data */
483 switch (keytype) {
484 /* direct keyframe data (these loops are exposed) */
485 case ALE_FCURVE: /* F-Curve */
486 return ANIM_fcurve_keyframes_loop(ked, static_cast<FCurve *>(data), key_ok, key_cb, fcu_cb);
487
488 /* indirect 'summaries' (these are not exposed directly)
489 * NOTE: must keep this code in sync with the drawing code and also the filtering code!
490 */
491 case ALE_GROUP: /* action group */
492 return agrp_keyframes_loop(ked, static_cast<bActionGroup *>(data), key_ok, key_cb, fcu_cb);
494 case ALE_ACTION_SLOT:
495 /* This function is only used in nlaedit_apply_scale_exec(). Since the NLA has no support for
496 * layered Actions in strips, there is no need to implement this here. */
497 return 0;
498 case ALE_ACT: /* action */
500 ked, static_cast<bAction *>(data), key_ok, key_cb, fcu_cb);
501 case ALE_OB: /* object */
502 return ob_keyframes_loop(ked, ads, static_cast<Object *>(data), key_ok, key_cb, fcu_cb);
503 case ALE_SCE: /* scene */
504 return scene_keyframes_loop(ked, ads, static_cast<Scene *>(data), key_ok, key_cb, fcu_cb);
505 case ALE_ALL: /* 'all' (DopeSheet summary) */
507 ked, static_cast<bAnimContext *>(data), key_ok, key_cb, fcu_cb);
508 }
509
510 return 0;
511}
512
515 KeyframeEditFunc callback_fn)
516{
517 ListBase anim_data = {nullptr, nullptr};
518
520 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
521
522 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
524 static_cast<FCurve *>(ale->key_data),
525 nullptr,
526 callback_fn,
528 ale->update |= ANIM_UPDATE_DEFAULT;
529 }
530
531 ANIM_animdata_update(ac, &anim_data);
532 ANIM_animdata_freelist(&anim_data);
533}
534
535/* ************************************************************************** */
536/* Keyframe Integrity Tools */
537
539{
540 ListBase anim_data = {nullptr, nullptr};
541 int filter;
542
543 /* filter animation data */
546 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
547
548 /* Loop over F-Curves that are likely to have been edited, and tag them to
549 * ensure the keyframes are in order and handles are in a valid position. */
550 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
552 }
553
554 /* free temp data */
555 ANIM_animdata_update(ac, &anim_data);
556 ANIM_animdata_freelist(&anim_data);
557}
558
559/* ************************************************************************** */
560/* BezTriple Validation Callbacks */
561
562/* ------------------------ */
563
565{
566 const bool handles_shown = (ked->iterflags & KEYFRAME_ITER_HANDLES_INVISIBLE) == 0;
567 if (!handles_shown) {
568 return false;
569 }
570 const bool handles_shown_only_selected = ked->iterflags &
572
573 return handles_shown_only_selected ? BEZT_ISSEL_ANY(bezt) : true;
574}
575
577 KeyframeEditData *ked,
578 BezTriple *bezt,
579 blender::FunctionRef<bool(KeyframeEditData *ked, BezTriple *bezt, const int index)> check)
580{
581 short ok = 0;
582 if (check(ked, bezt, 1)) {
583 ok |= KEYFRAME_OK_KEY;
584 }
585 if (ked && (ked->iterflags & KEYFRAME_ITER_INCL_HANDLES))
586 { /* Only act on visible items, so check handle visibility state. */
587 if (handles_visible(ked, bezt)) {
588 if (check(ked, bezt, 0)) {
589 ok |= KEYFRAME_OK_H1;
590 }
591 if (check(ked, bezt, 2)) {
592 ok |= KEYFRAME_OK_H2;
593 }
594 }
595 }
596 return ok;
597}
598
599/* ------------------------ */
600
602{
603 /* frame is stored in f1 property (this float accuracy check may need to be dropped?) */
604 const short ok = keyframe_ok_checks(
605 ked, bezt, [](KeyframeEditData *ked, BezTriple *bezt, int index) -> bool {
606 return IS_EQF(bezt->vec[index][0], ked->f1);
607 });
608
609 return ok;
610}
611
613{
614 const short ok = keyframe_ok_checks(
615 ked, bezt, [](KeyframeEditData *ked, BezTriple *bezt, int index) -> bool {
616 return (bezt->vec[index][0] > ked->f1) && (bezt->vec[index][0] < ked->f2);
617 });
618
619 return ok;
620}
621
622static short ok_bezier_selected(KeyframeEditData * /*ked*/, BezTriple *bezt)
623{
624 /* this macro checks all beztriple handles for selection...
625 * only one of the verts has to be selected for this to be ok...
626 */
627 if (BEZT_ISSEL_ANY(bezt)) {
628 return KEYFRAME_OK_ALL;
629 }
630 return 0;
631}
632
634{
635 /* This macro checks the beztriple key (f2) selection. */
636 if (BEZT_ISSEL_IDX(bezt, 1)) {
637 return KEYFRAME_OK_KEY;
638 }
639 return 0;
640}
641
643{
644 /* Value is stored in f1 property:
645 * - This float accuracy check may need to be dropped?
646 * - Should value be stored in f2 instead
647 * so that we won't have conflicts when using f1 for frames too?
648 */
649 const short ok = keyframe_ok_checks(
650 ked, bezt, [](KeyframeEditData *ked, BezTriple *bezt, int index) -> bool {
651 return IS_EQF(bezt->vec[index][1], ked->f1);
652 });
653
654 return ok;
655}
656
658{
659 /* value range is stored in float properties */
660 const short ok = keyframe_ok_checks(
661 ked, bezt, [](KeyframeEditData *ked, BezTriple *bezt, int index) -> bool {
662 return (bezt->vec[index][1] > ked->f1) && (bezt->vec[index][1] < ked->f2);
663 });
664
665 return ok;
666}
667
669{
670 /* rect is stored in data property (it's of type rectf, but may not be set) */
671 if (!ked->data) {
672 return 0;
673 }
674
675 const short ok = keyframe_ok_checks(
676 ked, bezt, [](KeyframeEditData *ked, BezTriple *bezt, int index) -> bool {
677 return BLI_rctf_isect_pt_v(static_cast<rctf *>(ked->data), bezt->vec[index]);
678 });
679
680 return ok;
681}
682
683bool keyframe_region_lasso_test(const KeyframeEdit_LassoData *data_lasso, const float xy[2])
684{
685 if (BLI_rctf_isect_pt_v(data_lasso->rectf_scaled, xy)) {
686 float xy_view[2];
687
688 BLI_rctf_transform_pt_v(data_lasso->rectf_view, data_lasso->rectf_scaled, xy_view, xy);
689
690 if (BLI_lasso_is_point_inside(data_lasso->mcoords, xy_view[0], xy_view[1], INT_MAX)) {
691 return true;
692 }
693 }
694
695 return false;
696}
697
699{
700 /* check for lasso customdata (KeyframeEdit_LassoData) */
701 if (!ked->data) {
702 return 0;
703 }
704
705 const short ok = keyframe_ok_checks(
706 ked, bezt, [](KeyframeEditData *ked, BezTriple *bezt, int index) -> bool {
707 return keyframe_region_lasso_test(static_cast<KeyframeEdit_LassoData *>(ked->data),
708 bezt->vec[index]);
709 });
710
711 return ok;
712}
713
715{
716 /* check for lasso customdata (KeyframeEdit_LassoData) */
717 if (ked->data) {
719 float pt[2];
720
721 /* late-binding remap of the x values (for summary channels) */
722 /* XXX: Ideally we reset, but it should be fine just leaving it as-is
723 * as the next channel will reset it properly, while the next summary-channel
724 * curve will also reset by itself...
725 */
727 data->rectf_scaled->xmin = ked->f1;
728 data->rectf_scaled->xmax = ked->f2;
729 }
730
731 /* only use the x-coordinate of the point; the y is the channel range... */
732 pt[0] = bezt->vec[1][0];
733 pt[1] = ked->channel_y;
734
736 return KEYFRAME_OK_KEY;
737 }
738 }
739 return 0;
740}
741
742bool keyframe_region_circle_test(const KeyframeEdit_CircleData *data_circle, const float xy[2])
743{
744 if (BLI_rctf_isect_pt_v(data_circle->rectf_scaled, xy)) {
745 float xy_view[2];
746
747 BLI_rctf_transform_pt_v(data_circle->rectf_view, data_circle->rectf_scaled, xy_view, xy);
748
749 xy_view[0] = xy_view[0] - data_circle->mval[0];
750 xy_view[1] = xy_view[1] - data_circle->mval[1];
751 return len_squared_v2(xy_view) < data_circle->radius_squared;
752 }
753
754 return false;
755}
756
758{
759 /* check for circle select customdata (KeyframeEdit_CircleData) */
760 if (!ked->data) {
761 return 0;
762 }
763
764 const short ok = keyframe_ok_checks(
765 ked, bezt, [](KeyframeEditData *ked, BezTriple *bezt, int index) -> bool {
766 return keyframe_region_circle_test(static_cast<KeyframeEdit_CircleData *>(ked->data),
767 bezt->vec[index]);
768 });
769
770 return ok;
771}
772
774{
775 /* check for circle select customdata (KeyframeEdit_CircleData) */
776 if (ked->data) {
778 float pt[2];
779
780 /* late-binding remap of the x values (for summary channels) */
781 /* XXX: Ideally we reset, but it should be fine just leaving it as-is
782 * as the next channel will reset it properly, while the next summary-channel
783 * curve will also reset by itself...
784 */
786 data->rectf_scaled->xmin = ked->f1;
787 data->rectf_scaled->xmax = ked->f2;
788 }
789
790 /* only use the x-coordinate of the point; the y is the channel range... */
791 pt[0] = bezt->vec[1][0];
792 pt[1] = ked->channel_y;
793
795 return KEYFRAME_OK_KEY;
796 }
797 }
798 return 0;
799}
800
802{
803 /* eEditKeyframes_Validate */
804 switch (mode) {
805 case BEZT_OK_FRAME:
806 /* only if bezt falls on the right frame (float) */
807 return ok_bezier_frame;
809 /* only if bezt falls within the specified frame range (floats) */
811 case BEZT_OK_SELECTED:
812 /* only if bezt is selected (any of f1, f2, f3) */
813 return ok_bezier_selected;
815 /* only if bezt is selected (f2 is enough) */
817 case BEZT_OK_VALUE:
818 /* only if bezt value matches (float) */
819 return ok_bezier_value;
821 /* only if bezier falls within the specified value range (floats) */
823 case BEZT_OK_REGION:
824 /* only if bezier falls within the specified rect (data -> rectf) */
825 return ok_bezier_region;
827 /* only if the point falls within KeyframeEdit_LassoData defined data */
830 /* only if the point falls within KeyframeEdit_CircleData defined data */
833 /* same as BEZT_OK_REGION_LASSO, but we're only using the x-value of the points */
836 /* same as BEZT_OK_REGION_CIRCLE, but we're only using the x-value of the points */
838 default: /* nothing was ok */
839 return nullptr;
840 }
841}
842
843/* ******************************************* */
844/* Assorted Utility Functions */
845
847{
848 /* only if selected */
849 if (bezt->f2 & SELECT) {
850 /* store average time in float 1 (only do rounding at last step) */
851 ked->f1 += bezt->vec[1][0];
852
853 /* store average value in float 2 (only do rounding at last step)
854 * - this isn't always needed, but some operators may also require this
855 */
856 ked->f2 += bezt->vec[1][1];
857
858 /* increment number of items */
859 ked->i1++;
860 }
861
862 return 0;
863}
864
866{
867 /* only if selected */
868 if ((bezt->f2 & SELECT) == 0) {
869 return 0;
870 }
871
872 CfraElem *ce = MEM_callocN<CfraElem>("cfraElem");
873 BLI_addtail(&ked->list, ce);
874
875 /* bAnimListElem so we can do NLA mapping, we want the cfra to be in "global" time */
876 bAnimListElem *ale = static_cast<bAnimListElem *>(ked->data);
877 if (ale != nullptr) {
878 ce->cfra = ANIM_nla_tweakedit_remap(ale, bezt->vec[1][0], NLATIME_CONVERT_MAP);
879 }
880 else {
881 ce->cfra = bezt->vec[1][0];
882 }
883
884 return 0;
885}
886
888{
889 KeyframeEditCD_Remap *rmap = static_cast<KeyframeEditCD_Remap *>(ked->data);
890 const float scale = (rmap->newMax - rmap->newMin) / (rmap->oldMax - rmap->oldMin);
891
892 /* perform transform on all three handles unless indicated otherwise */
893 /* TODO: need to include some checks for that */
894
895 bezt->vec[0][0] = scale * (bezt->vec[0][0] - rmap->oldMin) + rmap->newMin;
896 bezt->vec[1][0] = scale * (bezt->vec[1][0] - rmap->oldMin) + rmap->newMin;
897 bezt->vec[2][0] = scale * (bezt->vec[2][0] - rmap->oldMin) + rmap->newMin;
898}
899
900/* ******************************************* */
901/* Transform */
902
903/* snaps the keyframe to the nearest frame */
904static short snap_bezier_nearest(KeyframeEditData * /*ked*/, BezTriple *bezt)
905{
906 if (bezt->f2 & SELECT) {
907 BKE_fcurve_keyframe_move_time_with_handles(bezt, floorf(bezt->vec[1][0] + 0.5f));
908 }
909 return 0;
910}
911
912/* snaps the keyframe to the nearest second */
914{
915 const Scene *scene = ked->scene;
916 const float secf = float(FPS);
917
918 if (bezt->f2 & SELECT) {
919 BKE_fcurve_keyframe_move_time_with_handles(bezt, floorf(bezt->vec[1][0] / secf + 0.5f) * secf);
920 }
921 return 0;
922}
923
924/* snaps the keyframe to the current frame */
926{
927 const Scene *scene = ked->scene;
928 if (bezt->f2 & SELECT) {
930 }
931 return 0;
932}
933
934/* snaps the keyframe time to the nearest marker's frame */
936{
937 if (bezt->f2 & SELECT) {
939 bezt, float(ED_markers_find_nearest_marker_time(&ked->list, bezt->vec[1][0])));
940 }
941 return 0;
942}
943
944/* make the handles have the same value as the key */
946{
947 if (bezt->f2 & SELECT) {
948 bezt->vec[0][1] = bezt->vec[2][1] = bezt->vec[1][1];
949
950 if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) {
951 bezt->h1 = HD_ALIGN;
952 }
953 if (ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) {
954 bezt->h2 = HD_ALIGN;
955 }
956 }
957 return 0;
958}
959
960/* frame to snap to is stored in the custom data -> first float value slot */
962{
963 if (bezt->f2 & SELECT) {
965 }
966 return 0;
967}
968
969/* value to snap to is stored in the custom data -> first float value slot */
971{
972 if (bezt->f2 & SELECT) {
974 }
975 return 0;
976}
977
979{
980 /* eEditKeyframes_Snap */
981 switch (mode) {
982 case SNAP_KEYS_NEARFRAME: /* snap to nearest frame */
983 return snap_bezier_nearest;
984 case SNAP_KEYS_CURFRAME: /* snap to current frame */
985 return snap_bezier_cframe;
986 case SNAP_KEYS_NEARMARKER: /* snap to nearest marker */
988 case SNAP_KEYS_NEARSEC: /* snap to nearest second */
990 case SNAP_KEYS_HORIZONTAL: /* snap handles to same value */
992 case SNAP_KEYS_TIME: /* snap to given frame/time */
993 return snap_bezier_time;
994 case SNAP_KEYS_VALUE: /* snap to given value */
995 return snap_bezier_value;
996 default: /* just in case */
997 return snap_bezier_nearest;
998 }
999}
1000
1001/* --------- */
1002
1003static void mirror_bezier_xaxis_ex(BezTriple *bezt, const float center)
1004{
1005 for (int i = 0; i < 3; i++) {
1006 float diff = (center - bezt->vec[i][0]);
1007 bezt->vec[i][0] = (center + diff);
1008 }
1009 swap_v3_v3(bezt->vec[0], bezt->vec[2]);
1010
1011 std::swap(bezt->h1, bezt->h2);
1012 std::swap(bezt->f1, bezt->f3);
1013}
1014
1015static void mirror_bezier_yaxis_ex(BezTriple *bezt, const float center)
1016{
1017 for (int i = 0; i < 3; i++) {
1018 float diff = (center - bezt->vec[i][1]);
1019 bezt->vec[i][1] = (center + diff);
1020 }
1021}
1022
1024{
1025 const Scene *scene = ked->scene;
1026
1027 if (bezt->f2 & SELECT) {
1028 mirror_bezier_xaxis_ex(bezt, scene->r.cfra);
1029 }
1030
1031 return 0;
1032}
1033
1034static short mirror_bezier_yaxis(KeyframeEditData * /*ked*/, BezTriple *bezt)
1035{
1036 if (bezt->f2 & SELECT) {
1037 /* Yes, names are inverted, we are mirroring across y axis, hence along x axis... */
1038 mirror_bezier_xaxis_ex(bezt, 0.0f);
1039 }
1040
1041 return 0;
1042}
1043
1044static short mirror_bezier_xaxis(KeyframeEditData * /*ked*/, BezTriple *bezt)
1045{
1046 if (bezt->f2 & SELECT) {
1047 /* Yes, names are inverted, we are mirroring across x axis, hence along y axis... */
1048 mirror_bezier_yaxis_ex(bezt, 0.0f);
1049 }
1050
1051 return 0;
1052}
1053
1055{
1056 /* mirroring time stored in f1 */
1057 if (bezt->f2 & SELECT) {
1058 mirror_bezier_xaxis_ex(bezt, ked->f1);
1059 }
1060
1061 return 0;
1062}
1063
1065{
1066 /* value to mirror over is stored in f1 */
1067 if (bezt->f2 & SELECT) {
1068 mirror_bezier_xaxis_ex(bezt, ked->f1);
1069 }
1070
1071 return 0;
1072}
1073
1075{
1076 /* value to mirror over is stored in the custom data -> first float value slot */
1077 if (bezt->f2 & SELECT) {
1078 mirror_bezier_yaxis_ex(bezt, ked->f1);
1079 }
1080
1081 return 0;
1082}
1083
1085{
1086 switch (mode) {
1087 case MIRROR_KEYS_CURFRAME: /* mirror over current frame */
1088 return mirror_bezier_cframe;
1089 case MIRROR_KEYS_YAXIS: /* mirror over frame 0 */
1090 return mirror_bezier_yaxis;
1091 case MIRROR_KEYS_XAXIS: /* mirror over value 0 */
1092 return mirror_bezier_xaxis;
1093 case MIRROR_KEYS_MARKER: /* mirror over marker */
1094 return mirror_bezier_marker;
1095 case MIRROR_KEYS_TIME: /* mirror over frame/time */
1096 return mirror_bezier_time;
1097 case MIRROR_KEYS_VALUE: /* mirror over given value */
1098 return mirror_bezier_value;
1099 default: /* just in case */
1100 return mirror_bezier_yaxis;
1101 }
1102}
1103
1104/* ******************************************* */
1105/* Settings */
1106
1112#define ENSURE_HANDLES_MATCH(bezt) \
1113 if (bezt->h1 != bezt->h2) { \
1114 if (ELEM(bezt->h1, HD_ALIGN, HD_AUTO, HD_AUTO_ANIM)) { \
1115 bezt->h1 = HD_FREE; \
1116 } \
1117 if (ELEM(bezt->h2, HD_ALIGN, HD_AUTO, HD_AUTO_ANIM)) { \
1118 bezt->h2 = HD_FREE; \
1119 } \
1120 } \
1121 (void)0
1122
1123/* Sets the selected bezier handles to type 'auto' */
1124static short set_bezier_auto(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_AUTO;
1129 }
1130 else {
1131 if (bezt->f1 & SELECT) {
1132 bezt->h1 = HD_AUTO;
1133 }
1134 if (bezt->f3 & SELECT) {
1135 bezt->h2 = HD_AUTO;
1136 }
1137
1139 }
1140
1141 return 0;
1142}
1143
1144/* Sets the selected bezier handles to type 'auto-clamped'
1145 * NOTE: this is like auto above, but they're handled a bit different
1146 */
1148{
1149 /* If the key is selected, always apply to both handles. */
1150 if (bezt->f2 & SELECT) {
1151 bezt->h1 = bezt->h2 = HD_AUTO_ANIM;
1152 }
1153 else {
1154 if (bezt->f1 & SELECT) {
1155 bezt->h1 = HD_AUTO_ANIM;
1156 }
1157 if (bezt->f3 & SELECT) {
1158 bezt->h2 = HD_AUTO_ANIM;
1159 }
1160
1162 }
1163
1164 return 0;
1165}
1166
1167/* Sets the selected bezier handles to type 'vector'. */
1168static short set_bezier_vector(KeyframeEditData * /*ked*/, BezTriple *bezt)
1169{
1170 /* If the key is selected, always apply to both handles. */
1171 if (bezt->f2 & SELECT) {
1172 bezt->h1 = bezt->h2 = HD_VECT;
1173 }
1174 else {
1175 if (bezt->f1 & SELECT) {
1176 bezt->h1 = HD_VECT;
1177 }
1178 if (bezt->f3 & SELECT) {
1179 bezt->h2 = HD_VECT;
1180 }
1181 }
1182
1183 return 0;
1184}
1185
1192static short bezier_isfree(KeyframeEditData * /*ked*/, BezTriple *bezt)
1193{
1194 if ((bezt->f1 & SELECT) && (bezt->h1)) {
1195 return 1;
1196 }
1197 if ((bezt->f3 & SELECT) && (bezt->h2)) {
1198 return 1;
1199 }
1200 return 0;
1201}
1202
1203/* Sets selected bezier handles to type 'align' */
1204static short set_bezier_align(KeyframeEditData * /*ked*/, BezTriple *bezt)
1205{
1206 /* If the key is selected, always apply to both handles. */
1207 if (bezt->f2 & SELECT) {
1208 bezt->h1 = bezt->h2 = HD_ALIGN;
1209 }
1210 else {
1211 if (bezt->f1 & SELECT) {
1212 bezt->h1 = HD_ALIGN;
1213 }
1214 if (bezt->f3 & SELECT) {
1215 bezt->h2 = HD_ALIGN;
1216 }
1217 }
1218
1219 return 0;
1220}
1221
1222/* Sets selected bezier handles to type 'free'. */
1223static short set_bezier_free(KeyframeEditData * /*ked*/, BezTriple *bezt)
1224{
1225 /* If the key is selected, always apply to both handles. */
1226 if (bezt->f2 & SELECT) {
1227 bezt->h1 = bezt->h2 = HD_FREE;
1228 }
1229 else {
1230 if (bezt->f1 & SELECT) {
1231 bezt->h1 = HD_FREE;
1232 }
1233 if (bezt->f3 & SELECT) {
1234 bezt->h2 = HD_FREE;
1235 }
1236 }
1237
1238 return 0;
1239}
1240
1242{
1243 switch (mode) {
1244 case HD_AUTO: /* auto */
1245 return set_bezier_auto;
1246 case HD_AUTO_ANIM: /* auto clamped */
1248
1249 case HD_VECT: /* vector */
1250 return set_bezier_vector;
1251 case HD_FREE: /* free */
1252 return set_bezier_free;
1253 case HD_ALIGN: /* align */
1254 return set_bezier_align;
1255
1256 default: /* check for toggle free or align? */
1257 return bezier_isfree;
1258 }
1259}
1260
1261/* ------- */
1262
1263static short set_bezt_constant(KeyframeEditData * /*ked*/, BezTriple *bezt)
1264{
1265 if (bezt->f2 & SELECT) {
1266 bezt->ipo = BEZT_IPO_CONST;
1267 }
1268 return 0;
1269}
1270
1271static short set_bezt_linear(KeyframeEditData * /*ked*/, BezTriple *bezt)
1272{
1273 if (bezt->f2 & SELECT) {
1274 bezt->ipo = BEZT_IPO_LIN;
1275 }
1276 return 0;
1277}
1278
1279static short set_bezt_bezier(KeyframeEditData * /*ked*/, BezTriple *bezt)
1280{
1281 if (bezt->f2 & SELECT) {
1282 bezt->ipo = BEZT_IPO_BEZ;
1283 }
1284 return 0;
1285}
1286
1287static short set_bezt_back(KeyframeEditData * /*ked*/, BezTriple *bezt)
1288{
1289 if (bezt->f2 & SELECT) {
1290 bezt->ipo = BEZT_IPO_BACK;
1291 }
1292 return 0;
1293}
1294
1295static short set_bezt_bounce(KeyframeEditData * /*ked*/, BezTriple *bezt)
1296{
1297 if (bezt->f2 & SELECT) {
1298 bezt->ipo = BEZT_IPO_BOUNCE;
1299 }
1300 return 0;
1301}
1302
1303static short set_bezt_circle(KeyframeEditData * /*ked*/, BezTriple *bezt)
1304{
1305 if (bezt->f2 & SELECT) {
1306 bezt->ipo = BEZT_IPO_CIRC;
1307 }
1308 return 0;
1309}
1310
1311static short set_bezt_cubic(KeyframeEditData * /*ked*/, BezTriple *bezt)
1312{
1313 if (bezt->f2 & SELECT) {
1314 bezt->ipo = BEZT_IPO_CUBIC;
1315 }
1316 return 0;
1317}
1318
1319static short set_bezt_elastic(KeyframeEditData * /*ked*/, BezTriple *bezt)
1320{
1321 if (bezt->f2 & SELECT) {
1322 bezt->ipo = BEZT_IPO_ELASTIC;
1323 }
1324 return 0;
1325}
1326
1327static short set_bezt_expo(KeyframeEditData * /*ked*/, BezTriple *bezt)
1328{
1329 if (bezt->f2 & SELECT) {
1330 bezt->ipo = BEZT_IPO_EXPO;
1331 }
1332 return 0;
1333}
1334
1335static short set_bezt_quad(KeyframeEditData * /*ked*/, BezTriple *bezt)
1336{
1337 if (bezt->f2 & SELECT) {
1338 bezt->ipo = BEZT_IPO_QUAD;
1339 }
1340 return 0;
1341}
1342
1343static short set_bezt_quart(KeyframeEditData * /*ked*/, BezTriple *bezt)
1344{
1345 if (bezt->f2 & SELECT) {
1346 bezt->ipo = BEZT_IPO_QUART;
1347 }
1348 return 0;
1349}
1350
1351static short set_bezt_quint(KeyframeEditData * /*ked*/, BezTriple *bezt)
1352{
1353 if (bezt->f2 & SELECT) {
1354 bezt->ipo = BEZT_IPO_QUINT;
1355 }
1356 return 0;
1357}
1358
1359static short set_bezt_sine(KeyframeEditData * /*ked*/, BezTriple *bezt)
1360{
1361 if (bezt->f2 & SELECT) {
1362 bezt->ipo = BEZT_IPO_SINE;
1363 }
1364 return 0;
1365}
1366
1367static void handle_flatten(float vec[3][3], const int idx, const float direction[2])
1368{
1369 BLI_assert_msg(idx == 0 || idx == 2, "handle_flatten() expects a handle index");
1370
1371 add_v2_v2v2(vec[idx], vec[1], direction);
1372}
1373
1374static void handle_set_length(float vec[3][3], const int idx, const float handle_length)
1375{
1376 BLI_assert_msg(idx == 0 || idx == 2, "handle_set_length() expects a handle index");
1377
1378 float handle_direction[2];
1379 sub_v2_v2v2(handle_direction, vec[idx], vec[1]);
1380 normalize_v2_length(handle_direction, handle_length);
1381 add_v2_v2v2(vec[idx], vec[1], handle_direction);
1382}
1383
1385 const eEditKeyframes_Equalize mode,
1386 const float handle_length,
1387 const bool flatten)
1388{
1389 uint i;
1390 BezTriple *bezt;
1391 const float flat_direction_left[2] = {-handle_length, 0.0f};
1392 const float flat_direction_right[2] = {handle_length, 0.0f};
1393
1394 /* Loop through an F-Curves keyframes. */
1395 for (bezt = fcu->bezt, i = 0; i < fcu->totvert; bezt++, i++) {
1396 if ((bezt->f2 & SELECT) == 0) {
1397 continue;
1398 }
1399
1400 /* Perform handle equalization if mode is 'Both' or 'Left'. */
1401 if (mode & EQUALIZE_HANDLES_LEFT) {
1402 /* If left handle type is 'Auto', 'Auto Clamped', or 'Vector', convert handles to
1403 * 'Aligned'.
1404 */
1405 if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) {
1406 bezt->h1 = HD_ALIGN;
1407 bezt->h2 = HD_ALIGN;
1408 }
1409
1410 if (flatten) {
1411 handle_flatten(bezt->vec, 0, flat_direction_left);
1412 }
1413 else {
1414 handle_set_length(bezt->vec, 0, handle_length);
1415 }
1416 }
1417
1418 /* Perform handle equalization if mode is 'Both' or 'Right'. */
1419 if (mode & EQUALIZE_HANDLES_RIGHT) {
1420 /* If right handle type is 'Auto', 'Auto Clamped', or 'Vector', convert handles to
1421 * 'Aligned'. */
1422 if (ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) {
1423 bezt->h1 = HD_ALIGN;
1424 bezt->h2 = HD_ALIGN;
1425 }
1426
1427 if (flatten) {
1428 handle_flatten(bezt->vec, 2, flat_direction_right);
1429 }
1430 else {
1431 handle_set_length(bezt->vec, 2, handle_length);
1432 }
1433 }
1434 }
1435}
1436
1438{
1439 switch (mode) {
1440 /* interpolation */
1441 case BEZT_IPO_CONST: /* constant */
1442 return set_bezt_constant;
1443 case BEZT_IPO_LIN: /* linear */
1444 return set_bezt_linear;
1445
1446 /* easing */
1447 case BEZT_IPO_BACK:
1448 return set_bezt_back;
1449 case BEZT_IPO_BOUNCE:
1450 return set_bezt_bounce;
1451 case BEZT_IPO_CIRC:
1452 return set_bezt_circle;
1453 case BEZT_IPO_CUBIC:
1454 return set_bezt_cubic;
1455 case BEZT_IPO_ELASTIC:
1456 return set_bezt_elastic;
1457 case BEZT_IPO_EXPO:
1458 return set_bezt_expo;
1459 case BEZT_IPO_QUAD:
1460 return set_bezt_quad;
1461 case BEZT_IPO_QUART:
1462 return set_bezt_quart;
1463 case BEZT_IPO_QUINT:
1464 return set_bezt_quint;
1465 case BEZT_IPO_SINE:
1466 return set_bezt_sine;
1467
1468 default: /* bezier */
1469 return set_bezt_bezier;
1470 }
1471}
1472
1473/* ------- */
1474
1476{
1477 if (bezt->f2 & SELECT) {
1479 }
1480 return 0;
1481}
1482
1484{
1485 if (bezt->f2 & SELECT) {
1487 }
1488 return 0;
1489}
1490
1491static short set_keytype_extreme(KeyframeEditData * /*ked*/, BezTriple *bezt)
1492{
1493 if (bezt->f2 & SELECT) {
1495 }
1496 return 0;
1497}
1498
1499static short set_keytype_jitter(KeyframeEditData * /*ked*/, BezTriple *bezt)
1500{
1501 if (bezt->f2 & SELECT) {
1503 }
1504 return 0;
1505}
1506
1508{
1509 if (bezt->f2 & SELECT) {
1511 }
1512 return 0;
1513}
1514
1516{
1517 if (bezt->f2 & SELECT) {
1519 }
1520 return 0;
1521}
1522
1524{
1525 switch (keyframe_type) {
1527 return set_keytype_breakdown;
1528
1530 return set_keytype_extreme;
1531
1533 return set_keytype_jitter;
1534
1537
1539 return set_keytype_keyframe;
1540
1542 return set_keytype_generated;
1543 }
1544
1546 return nullptr;
1547}
1548
1549/* ------- */
1550
1552{
1553 if (bezt->f2 & SELECT) {
1554 bezt->easing = BEZT_IPO_EASE_IN;
1555 }
1556 return 0;
1557}
1558
1560{
1561 if (bezt->f2 & SELECT) {
1562 bezt->easing = BEZT_IPO_EASE_OUT;
1563 }
1564 return 0;
1565}
1566
1568{
1569 if (bezt->f2 & SELECT) {
1571 }
1572 return 0;
1573}
1574
1576{
1577 if (bezt->f2 & SELECT) {
1578 bezt->easing = BEZT_IPO_EASE_AUTO;
1579 }
1580 return 0;
1581}
1582
1584{
1585 switch (mode) {
1586 case BEZT_IPO_EASE_IN: /* ease in */
1587 return set_easingtype_easein;
1588
1589 case BEZT_IPO_EASE_OUT: /* ease out */
1591
1592 case BEZT_IPO_EASE_IN_OUT: /* both */
1594
1595 default: /* auto */
1597 }
1598}
1599
1600/* ******************************************* */
1601/* Selection */
1602
1604{
1605 /* Only act on visible items, so check handle visibility state. */
1606 /* if we've got info on what to select, use it, otherwise select all */
1607 if ((ked) && (ked->iterflags & KEYFRAME_ITER_INCL_HANDLES) && handles_visible(ked, bezt)) {
1608 if (ked->curflags & KEYFRAME_OK_KEY) {
1609 bezt->f2 |= SELECT;
1610 }
1611 if (ked->curflags & KEYFRAME_OK_H1) {
1612 bezt->f1 |= SELECT;
1613 }
1614 if (ked->curflags & KEYFRAME_OK_H2) {
1615 bezt->f3 |= SELECT;
1616 }
1617 }
1618 else {
1619 BEZT_SEL_ALL(bezt);
1620 }
1621
1622 return 0;
1623}
1624
1626{
1627 /* Only act on visible items, so check handle visibility state. */
1628 /* if we've got info on what to deselect, use it, otherwise deselect all */
1629 if ((ked) && (ked->iterflags & KEYFRAME_ITER_INCL_HANDLES) && handles_visible(ked, bezt)) {
1630 if (ked->curflags & KEYFRAME_OK_KEY) {
1631 bezt->f2 &= ~SELECT;
1632 }
1633 if (ked->curflags & KEYFRAME_OK_H1) {
1634 bezt->f1 &= ~SELECT;
1635 }
1636 if (ked->curflags & KEYFRAME_OK_H2) {
1637 bezt->f3 &= ~SELECT;
1638 }
1639 }
1640 else {
1641 BEZT_DESEL_ALL(bezt);
1642 }
1643
1644 return 0;
1645}
1646
1648{
1649 /* Invert the selection for the whole bezier triple */
1650 bezt->f2 ^= SELECT;
1651 if (bezt->f2 & SELECT) {
1652 bezt->f1 |= SELECT;
1653 bezt->f3 |= SELECT;
1654 }
1655 else {
1656 bezt->f1 &= ~SELECT;
1657 bezt->f3 &= ~SELECT;
1658 }
1659 return 0;
1660}
1661
1663{
1664 switch (selectmode) {
1665 case SELECT_ADD: /* add */
1666 return select_bezier_add;
1667 case SELECT_SUBTRACT: /* subtract */
1669 case SELECT_INVERT: /* invert */
1670 return select_bezier_invert;
1671 default: /* replace (need to clear all, then add) */
1672 return select_bezier_add;
1673 }
1674}
1675
1676/* ******************************************* */
1677/* Selection Maps */
1678
1679/* Selection maps are simply fancy names for char arrays that store on/off
1680 * info for whether the selection status. The main purpose for these is to
1681 * allow extra info to be tagged to the keyframes without influencing their
1682 * values or having to be removed later.
1683 */
1684
1685/* ----------- */
1686
1688{
1689 const FCurve *fcu = ked->fcu;
1690 char *map = static_cast<char *>(ked->data);
1691 int i = ked->curIndex;
1692
1693 /* if current is selected, just make sure it stays this way */
1694 if (BEZT_ISSEL_ANY(bezt)) {
1695 map[i] = 1;
1696 return 0;
1697 }
1698
1699 /* if previous is selected, that means that selection should extend across */
1700 if (i > 0) {
1701 BezTriple *prev = bezt - 1;
1702
1703 if (BEZT_ISSEL_ANY(prev)) {
1704 map[i] = 1;
1705 return 0;
1706 }
1707 }
1708
1709 /* if next is selected, that means that selection should extend across */
1710 if (i < (fcu->totvert - 1)) {
1711 BezTriple *next = bezt + 1;
1712
1713 if (BEZT_ISSEL_ANY(next)) {
1714 map[i] = 1;
1715 return 0;
1716 }
1717 }
1718
1719 return 0;
1720}
1721
1723{
1724 const FCurve *fcu = ked->fcu;
1725 char *map = static_cast<char *>(ked->data);
1726 int i = ked->curIndex;
1727
1728 /* if current is selected, check the left/right keyframes
1729 * since it might need to be deselected (but otherwise no)
1730 */
1731 if (BEZT_ISSEL_ANY(bezt)) {
1732 /* if previous is not selected, we're on the tip of an iceberg */
1733 if (i > 0) {
1734 BezTriple *prev = bezt - 1;
1735
1736 if (BEZT_ISSEL_ANY(prev) == 0) {
1737 return 0;
1738 }
1739 }
1740 else if (i == 0) {
1741 /* current keyframe is selected at an endpoint, so should get deselected */
1742 return 0;
1743 }
1744
1745 /* if next is not selected, we're on the tip of an iceberg */
1746 if (i < (fcu->totvert - 1)) {
1747 BezTriple *next = bezt + 1;
1748
1749 if (BEZT_ISSEL_ANY(next) == 0) {
1750 return 0;
1751 }
1752 }
1753 else if (i == (fcu->totvert - 1)) {
1754 /* current keyframe is selected at an endpoint, so should get deselected */
1755 return 0;
1756 }
1757
1758 /* if we're still here, that means that keyframe should remain untouched */
1759 map[i] = 1;
1760 }
1761
1762 return 0;
1763}
1764
1766{
1767 switch (mode) {
1768 case SELMAP_LESS: /* less */
1770
1771 case SELMAP_MORE: /* more */
1772 default:
1774 }
1775}
1776
1777/* ----------- */
1778
1780{
1781 const char *map = static_cast<char *>(ked->data);
1782 short on = map[ked->curIndex];
1783
1784 /* select or deselect based on whether the map allows it or not */
1785 if (on) {
1786 BEZT_SEL_ALL(bezt);
1787 }
1788 else {
1789 BEZT_DESEL_ALL(bezt);
1790 }
1791
1792 return 0;
1793}
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)
short ANIM_animchanneldata_keyframes_loop(KeyframeEditData *ked, bDopeSheet *ads, void *data, int keytype, KeyframeEditFunc key_ok, KeyframeEditFunc key_cb, FcuEditFunc fcu_cb)
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