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