Blender V5.0
keyframes_keylist.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009 Blender Authors, Joshua Leung. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9/* System includes ----------------------------------------------------- */
10
11#include <algorithm>
12#include <cfloat>
13#include <cstdlib>
14#include <cstring>
15#include <functional>
16#include <optional>
17
18#include "MEM_guardedalloc.h"
19
20#include "BLI_array.hh"
21#include "BLI_bounds_types.hh"
22#include "BLI_listbase.h"
23#include "BLI_utildefines.h"
24
25#include "DNA_anim_types.h"
26#include "DNA_cachefile_types.h"
28#include "DNA_mask_types.h"
29#include "DNA_object_types.h"
30#include "DNA_scene_types.h"
31#include "DNA_sequence_types.h"
32
33#include "BKE_fcurve.hh"
34#include "BKE_grease_pencil.hh"
35
36#include "ED_anim_api.hh"
38
39#include "SEQ_retiming.hh"
40
41#include "ANIM_action.hh"
42
43using namespace blender;
44
45/* *************************** Keyframe Processing *************************** */
46
47/* ActKeyColumns (Keyframe Columns) ------------------------------------------ */
48
49BLI_INLINE bool is_cfra_eq(const float a, const float b)
50{
52}
53
54BLI_INLINE bool is_cfra_lt(const float a, const float b)
55{
56 return (b - a) > BEZT_BINARYSEARCH_THRESH;
57}
58
59/* --------------- */
60
69
71 /* Number of ActKeyColumn's in the keylist. */
72 size_t column_len = 0;
73
75
76 /* Before initializing the runtime, the key_columns list base is used to quickly add columns.
77 * Contains `ActKeyColumn`. Should not be used after runtime is initialized. */
78 ListBase /*ActKeyColumn*/ key_columns;
79 /* Last accessed column in the key_columns list base. Inserting columns are typically done in
80 * order. The last accessed column is used as starting point to search for a location to add or
81 * update the next column. */
82 std::optional<ActKeyColumn *> last_accessed_column = std::nullopt;
83
84 struct {
85 /* When initializing the runtime the columns from the list base `AnimKeyList.key_columns` are
86 * transferred to an array to support binary searching and index based access. */
88 /* Wrapper around runtime.key_columns so it can still be accessed as a ListBase.
89 * Elements are owned by `runtime.key_columns`. */
90 ListBase /*ActKeyColumn*/ list_wrapper;
92
94 {
95 BLI_listbase_clear(&this->key_columns);
96 BLI_listbase_clear(&this->runtime.list_wrapper);
97 }
98
100 {
101 BLI_freelistN(&this->key_columns);
102 BLI_listbase_clear(&this->runtime.list_wrapper);
103 }
104
105 MEM_CXX_CLASS_ALLOC_FUNCS("editors:AnimKeylist")
106};
107
109{
110 AnimKeylist *keylist = new AnimKeylist();
111 return keylist;
112}
113
115{
116 BLI_assert(keylist);
117 delete keylist;
118}
119
121{
122 size_t index;
123 LISTBASE_FOREACH_INDEX (ActKeyColumn *, key, &keylist->key_columns, index) {
124 keylist->runtime.key_columns[index] = *key;
125 }
126}
127
129{
130 for (size_t index = 0; index < keylist->column_len; index++) {
131 const bool is_first = (index == 0);
132 keylist->runtime.key_columns[index].prev = is_first ? nullptr :
133 &keylist->runtime.key_columns[index - 1];
134 const bool is_last = (index == keylist->column_len - 1);
135 keylist->runtime.key_columns[index].next = is_last ? nullptr :
136 &keylist->runtime.key_columns[index + 1];
137 }
138}
139
141{
142 if (ED_keylist_is_empty(keylist)) {
144 return;
145 }
146
147 keylist->runtime.list_wrapper.first = keylist->runtime.key_columns.data();
148 keylist->runtime.list_wrapper.last = &keylist->runtime.key_columns[keylist->column_len - 1];
149}
150
152{
154
156
157 /* Convert linked list to array to support fast searching. */
159 /* Ensure that the array can also be used as a listbase for external usages. */
162
163 keylist->is_runtime_initialized = true;
164}
165
167{
169 keylist->last_accessed_column.reset();
170}
171
173{
174 if (keylist->is_runtime_initialized) {
175 return;
176 }
177 keylist_runtime_init(keylist);
178}
179
180static const ActKeyColumn *keylist_find_lower_bound(const AnimKeylist *keylist, const float cfra)
181{
183 const ActKeyColumn *begin = std::begin(keylist->runtime.key_columns);
184 const ActKeyColumn *end = std::end(keylist->runtime.key_columns);
185 ActKeyColumn value;
186 value.cfra = cfra;
187
188 const ActKeyColumn *found_column = std::lower_bound(
189 begin, end, value, [](const ActKeyColumn &column, const ActKeyColumn &other) {
190 return is_cfra_lt(column.cfra, other.cfra);
191 });
192 return found_column;
193}
194
195static const ActKeyColumn *keylist_find_upper_bound(const AnimKeylist *keylist, const float cfra)
196{
198 const ActKeyColumn *begin = std::begin(keylist->runtime.key_columns);
199 const ActKeyColumn *end = std::end(keylist->runtime.key_columns);
200 ActKeyColumn value;
201 value.cfra = cfra;
202
203 const ActKeyColumn *found_column = std::upper_bound(
204 begin, end, value, [](const ActKeyColumn &column, const ActKeyColumn &other) {
205 return is_cfra_lt(column.cfra, other.cfra);
206 });
207 return found_column;
208}
209
210const ActKeyColumn *ED_keylist_find_exact(const AnimKeylist *keylist, const float cfra)
211{
213 "ED_keylist_prepare_for_direct_access needs to be called before searching.");
214
215 if (ED_keylist_is_empty(keylist)) {
216 return nullptr;
217 }
218
219 const ActKeyColumn *found_column = keylist_find_lower_bound(keylist, cfra);
220
221 const ActKeyColumn *end = std::end(keylist->runtime.key_columns);
222 if (found_column == end) {
223 return nullptr;
224 }
225 if (is_cfra_eq(found_column->cfra, cfra)) {
226 return found_column;
227 }
228 return nullptr;
229}
230
231const ActKeyColumn *ED_keylist_find_next(const AnimKeylist *keylist, const float cfra)
232{
234 "ED_keylist_prepare_for_direct_access needs to be called before searching.");
235
236 if (ED_keylist_is_empty(keylist)) {
237 return nullptr;
238 }
239
240 const ActKeyColumn *found_column = keylist_find_upper_bound(keylist, cfra);
241
242 const ActKeyColumn *end = std::end(keylist->runtime.key_columns);
243 if (found_column == end) {
244 return nullptr;
245 }
246 return found_column;
247}
248
249const ActKeyColumn *ED_keylist_find_prev(const AnimKeylist *keylist, const float cfra)
250{
252 "ED_keylist_prepare_for_direct_access needs to be called before searching.");
253
254 if (ED_keylist_is_empty(keylist)) {
255 return nullptr;
256 }
257
258 const ActKeyColumn *end = std::end(keylist->runtime.key_columns);
259 const ActKeyColumn *found_column = keylist_find_lower_bound(keylist, cfra);
260
261 if (found_column == end) {
262 /* Nothing found, return the last item. */
263 return end - 1;
264 }
265
266 const ActKeyColumn *prev_column = found_column->prev;
267 return prev_column;
268}
269
270const ActKeyColumn *ED_keylist_find_closest(const AnimKeylist *keylist, const float cfra)
271{
273 "ED_keylist_prepare_for_direct_access needs to be called before searching.");
274 if (ED_keylist_is_empty(keylist)) {
275 return nullptr;
276 }
277 /* Need to check against #BEZT_BINARYSEARCH_THRESH because #ED_keylist_find_prev does so as well.
278 * Not doing that here could cause that function to return a nullptr. */
279 if (cfra - keylist->runtime.key_columns.first().cfra < BEZT_BINARYSEARCH_THRESH) {
280 return &keylist->runtime.key_columns.first();
281 }
282 if (cfra - keylist->runtime.key_columns.last().cfra > BEZT_BINARYSEARCH_THRESH) {
283 keylist->runtime.key_columns.last();
284 }
285 const ActKeyColumn *prev = ED_keylist_find_prev(keylist, cfra);
286 BLI_assert_msg(prev != nullptr,
287 "This should exist since we checked for cfra bounds just before");
288 /* This could be a nullptr though. */
289 const ActKeyColumn *next = prev->next;
290
291 if (!next) {
292 return prev;
293 }
294
295 const float prev_delta = cfra - prev->cfra;
296 const float next_delta = next->cfra - cfra;
297 /* `prev_delta` and `next_delta` can both be 0 if the given `cfra` is exactly at a key column. */
298
299 if (prev_delta <= next_delta) {
300 return prev;
301 }
302 return next;
303}
304
306 const Bounds<float> frame_range)
307{
309 "ED_keylist_prepare_for_direct_access needs to be called before searching.");
310
311 if (ED_keylist_is_empty(keylist)) {
312 return nullptr;
313 }
314
315 const ActKeyColumn *column = keylist_find_lower_bound(keylist, frame_range.min);
316 const ActKeyColumn *end = std::end(keylist->runtime.key_columns);
317 if (column == end) {
318 return nullptr;
319 }
320 if (column->cfra >= frame_range.max) {
321 return nullptr;
322 }
323 return column;
324}
325
327{
329 keylist->is_runtime_initialized,
330 "ED_keylist_prepare_for_direct_access needs to be called before accessing array.");
331 return keylist->runtime.key_columns.data();
332}
333
335{
336 return keylist->column_len;
337}
338
340{
341 return keylist->column_len == 0;
342}
343
345{
346 if (keylist->is_runtime_initialized) {
347 return &keylist->runtime.list_wrapper;
348 }
349 return &keylist->key_columns;
350}
351
352static void keylist_first_last(const AnimKeylist *keylist,
353 const ActKeyColumn **first_column,
354 const ActKeyColumn **last_column)
355{
356 if (keylist->is_runtime_initialized) {
357 *first_column = keylist->runtime.key_columns.data();
358 *last_column = &keylist->runtime.key_columns[keylist->column_len - 1];
359 }
360 else {
361 *first_column = static_cast<const ActKeyColumn *>(keylist->key_columns.first);
362 *last_column = static_cast<const ActKeyColumn *>(keylist->key_columns.last);
363 }
364}
365
367{
368 BLI_assert(r_frame_range);
369
370 if (ED_keylist_is_empty(keylist)) {
371 return false;
372 }
373
374 const ActKeyColumn *first_column;
375 const ActKeyColumn *last_column;
376 keylist_first_last(keylist, &first_column, &last_column);
377 r_frame_range->min = first_column->cfra;
378 r_frame_range->max = last_column->cfra;
379
380 return true;
381}
382
384{
385 BLI_assert(r_frame_range);
386
387 if (ED_keylist_is_empty(keylist)) {
388 return false;
389 }
390
391 const ActKeyColumn *first_column;
392 const ActKeyColumn *last_column;
393 keylist_first_last(keylist, &first_column, &last_column);
394 while (first_column && !(first_column->sel & SELECT)) {
395 first_column = first_column->next;
396 }
397 while (last_column && !(last_column->sel & SELECT)) {
398 last_column = last_column->prev;
399 }
400 if (!first_column || !last_column || first_column == last_column) {
401 return false;
402 }
403 r_frame_range->min = first_column->cfra;
404 r_frame_range->max = last_column->cfra;
405
406 return true;
407}
408
409/* Set of references to three logically adjacent keys. */
411 /* Current keyframe. */
413
414 /* Logical neighbors. May be nullptr. */
416};
417
418/* Categorize the interpolation & handle type of the keyframe. */
420{
421 if (bezt->h1 == HD_AUTO_ANIM && bezt->h2 == HD_AUTO_ANIM) {
423 }
424 if (ELEM(bezt->h1, HD_AUTO_ANIM, HD_AUTO) && ELEM(bezt->h2, HD_AUTO_ANIM, HD_AUTO)) {
426 }
427 if (bezt->h1 == HD_VECT && bezt->h2 == HD_VECT) {
429 }
430 if (ELEM(HD_FREE, bezt->h1, bezt->h2)) {
432 }
434}
435
436/* Determine if the keyframe is an extreme by comparing with neighbors.
437 * Ends of fixed-value sections and of the whole curve are also marked.
438 */
440{
441 if (chain->prev == nullptr && chain->next == nullptr) {
443 }
444
445 /* Keyframe values for the current one and neighbors. */
446 const float cur_y = chain->cur->vec[1][1];
447 float prev_y = cur_y, next_y = cur_y;
448
449 if (chain->prev && !IS_EQF(cur_y, chain->prev->vec[1][1])) {
450 prev_y = chain->prev->vec[1][1];
451 }
452 if (chain->next && !IS_EQF(cur_y, chain->next->vec[1][1])) {
453 next_y = chain->next->vec[1][1];
454 }
455
456 /* Static hold. */
457 if (prev_y == cur_y && next_y == cur_y) {
459 }
460
461 /* Middle of an incline. */
462 if ((prev_y < cur_y && next_y > cur_y) || (prev_y > cur_y && next_y < cur_y)) {
464 }
465
466 /* Bezier handle values for the overshoot check. */
467 const bool l_bezier = chain->prev && chain->prev->ipo == BEZT_IPO_BEZ;
468 const bool r_bezier = chain->next && chain->cur->ipo == BEZT_IPO_BEZ;
469 const float handle_l = l_bezier ? chain->cur->vec[0][1] : cur_y;
470 const float handle_r = r_bezier ? chain->cur->vec[2][1] : cur_y;
471
472 /* Detect extremes. One of the neighbors is allowed to be equal to current. */
473 if (prev_y < cur_y || next_y < cur_y) {
474 const bool is_overshoot = (handle_l > cur_y || handle_r > cur_y);
475
477 (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0));
478 }
479
480 if (prev_y > cur_y || next_y > cur_y) {
481 const bool is_overshoot = (handle_l < cur_y || handle_r < cur_y);
482
484 (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0));
485 }
486
488}
489
490/* New node callback used for building ActKeyColumns from BezTripleChain */
492{
493 ActKeyColumn *ak = MEM_callocN<ActKeyColumn>("ActKeyColumn");
494 const BezTripleChain *chain = static_cast<const BezTripleChain *>(data);
495 const BezTriple *bezt = chain->cur;
496
497 /* store settings based on state of BezTriple */
498 ak->cfra = bezt->vec[1][0];
499 ak->sel = BEZT_ISSEL_ANY(bezt) ? SELECT : 0;
500 ak->key_type = BEZKEYTYPE(bezt);
501 ak->handle_type = bezt_handle_type(bezt);
502 ak->extreme_type = bezt_extreme_type(chain);
503
504 /* count keyframes in this column */
505 ak->totkey = 1;
506
507 return ak;
508}
509
510/* Node updater callback used for building ActKeyColumns from BezTripleChain */
511static void nupdate_ak_bezt(ActKeyColumn *ak, void *data)
512{
513 const BezTripleChain *chain = static_cast<const BezTripleChain *>(data);
514 const BezTriple *bezt = chain->cur;
515
516 /* set selection status and 'touched' status */
517 if (BEZT_ISSEL_ANY(bezt)) {
518 ak->sel = SELECT;
519 }
520
521 ak->totkey++;
522
523 /* For keyframe type, 'proper' keyframes have priority over breakdowns
524 * (and other types for now). */
525 if (BEZKEYTYPE(bezt) == BEZT_KEYTYPE_KEYFRAME) {
527 }
528
529 /* For interpolation type, select the highest value (enum is sorted). */
531
532 /* For extremes, detect when combining different states. */
533 const char new_extreme = bezt_extreme_type(chain);
534
535 if (new_extreme != ak->extreme_type) {
536 /* Replace the flat status without adding mixed. */
538 ak->extreme_type = new_extreme;
539 }
540 else if (new_extreme != KEYFRAME_EXTREME_FLAT) {
541 ak->extreme_type |= (new_extreme | KEYFRAME_EXTREME_MIXED);
542 }
543 }
544}
545
546/* ......... */
547/* New node callback used for building ActKeyColumns from GPencil frames */
549{
550 ActKeyColumn *ak = MEM_callocN<ActKeyColumn>("ActKeyColumnCel");
551 GreasePencilCel &cel = *static_cast<GreasePencilCel *>(data);
552
553 /* Store settings based on state of BezTriple */
554 ak->cfra = cel.frame_number;
555 ak->sel = (cel.frame.flag & SELECT) != 0;
557
558 /* Count keyframes in this column */
559 ak->totkey = 1;
560 /* Set as visible block. */
561 ak->totblock = 1;
562 ak->block.sel = ak->sel;
564
565 return ak;
566}
567
568/* Node updater callback used for building ActKeyColumns from GPencil frames */
569static void nupdate_ak_cel(ActKeyColumn *ak, void *data)
570{
571 GreasePencilCel &cel = *static_cast<GreasePencilCel *>(data);
572
573 /* Update selection status. */
574 if (cel.frame.flag & GP_FRAME_SELECTED) {
575 ak->sel = SELECT;
576 }
577
578 ak->totkey++;
579
580 /* Update keytype status. */
581 if (cel.frame.type == BEZT_KEYTYPE_KEYFRAME) {
583 }
584}
585
586/* ......... */
587
588/* New node callback used for building ActKeyColumns from GPencil frames. */
590{
591 ActKeyColumn *ak = MEM_callocN<ActKeyColumn>("ActKeyColumnGPF");
592 const bGPDframe *gpf = (bGPDframe *)data;
593
594 /* store settings based on state of BezTriple */
595 ak->cfra = gpf->framenum;
596 ak->sel = (gpf->flag & GP_FRAME_SELECT) ? SELECT : 0;
598
599 /* Count keyframes in this column. */
600 ak->totkey = 1;
601 /* Set as visible block. */
602 ak->totblock = 1;
603 ak->block.sel = ak->sel;
605
606 return ak;
607}
608
609/* Node updater callback used for building ActKeyColumns from GPencil frames. */
610static void nupdate_ak_gpframe(ActKeyColumn *ak, void *data)
611{
612 bGPDframe *gpf = static_cast<bGPDframe *>(data);
613
614 /* Set selection status and 'touched' status. */
615 if (gpf->flag & GP_FRAME_SELECT) {
616 ak->sel = SELECT;
617 }
618
619 ak->totkey++;
620
621 /* For keyframe type, 'proper' keyframes have priority over breakdowns
622 * (and other types for now). */
623 if (gpf->key_type == BEZT_KEYTYPE_KEYFRAME) {
625 }
626}
627
628/* ......... */
629
630/* Extra struct to pass the timeline frame since the retiming key doesn't contain that and we would
631 * need the scene to get it. */
636
637/* New node callback used for building ActKeyColumns from Sequencer keys. */
639{
640 ActKeyColumn *ak = MEM_callocN<ActKeyColumn>("ActKeyColumnGPF");
641 const SeqAllocateData *allocate_data = (SeqAllocateData *)data;
642 const SeqRetimingKey *timing_key = allocate_data->key;
643
644 /* store settings based on state of BezTriple */
645 ak->cfra = allocate_data->timeline_frame;
646 ak->sel = (timing_key->flag & SEQ_KEY_SELECTED) ? SELECT : 0;
647 ak->key_type = eBezTriple_KeyframeType::BEZT_KEYTYPE_KEYFRAME;
648
649 /* Count keyframes in this column. */
650 ak->totkey = 1;
651 /* Set as visible block. */
652 ak->totblock = 1;
653 ak->block.sel = ak->sel;
654 ak->block.flag = 0;
655
656 return ak;
657}
658
659/* Node updater callback used for building ActKeyColumns from Sequencer keys. */
660static void nupdate_ak_seqframe(ActKeyColumn *ak, void *data)
661{
662 SeqAllocateData *allocate_data = static_cast<SeqAllocateData *>(data);
663
664 /* Set selection status and 'touched' status. */
665 if (allocate_data->key->flag & SEQ_KEY_SELECTED) {
666 ak->sel = SELECT;
667 }
668
669 ak->totkey++;
670}
671
672/* ......... */
673
674/* New node callback used for building ActKeyColumns from GPencil frames */
676{
677 ActKeyColumn *ak = MEM_callocN<ActKeyColumn>("ActKeyColumnGPF");
678 const MaskLayerShape *masklay_shape = (const MaskLayerShape *)data;
679
680 /* Store settings based on state of BezTriple. */
681 ak->cfra = masklay_shape->frame;
682 ak->sel = (masklay_shape->flag & MASK_SHAPE_SELECT) ? SELECT : 0;
683
684 /* Count keyframes in this column. */
685 ak->totkey = 1;
686
687 return ak;
688}
689
690/* Node updater callback used for building ActKeyColumns from GPencil frames */
692{
693 MaskLayerShape *masklay_shape = static_cast<MaskLayerShape *>(data);
694
695 /* Set selection status and 'touched' status. */
696 if (masklay_shape->flag & MASK_SHAPE_SELECT) {
697 ak->sel = SELECT;
698 }
699
700 ak->totkey++;
701}
702
703/* --------------- */
704using KeylistCreateColumnFunction = std::function<ActKeyColumn *(void *userdata)>;
705using KeylistUpdateColumnFunction = std::function<void(ActKeyColumn *, void *)>;
706
707/* `keylist_find_neighbor_front_to_back` is called before the runtime can be initialized so we
708 * cannot use bin searching. */
710{
711 while (cursor->next && cursor->next->cfra <= cfra) {
712 cursor = cursor->next;
713 }
714 return cursor;
715}
716
717/* `keylist_find_neighbor_back_to_front` is called before the runtime can be initialized so we
718 * cannot use bin searching. */
720{
721 while (cursor->prev && cursor->prev->cfra >= cfra) {
722 cursor = cursor->prev;
723 }
724 return cursor;
725}
726
727/*
728 * `keylist_find_exact_or_neighbor_column` is called before the runtime can be initialized so
729 * we cannot use bin searching.
730 *
731 * This function is called to add or update columns in the keylist.
732 * Typically columns are sorted by frame number so keeping track of the last_accessed_column
733 * reduces searching.
734 */
736{
738 if (ED_keylist_is_empty(keylist)) {
739 return nullptr;
740 }
741
742 ActKeyColumn *cursor = keylist->last_accessed_column.value_or(
743 static_cast<ActKeyColumn *>(keylist->key_columns.first));
744 if (!is_cfra_eq(cursor->cfra, cfra)) {
745 const bool walking_direction_front_to_back = cursor->cfra <= cfra;
746 if (walking_direction_front_to_back) {
747 cursor = keylist_find_neighbor_front_to_back(cursor, cfra);
748 }
749 else {
750 cursor = keylist_find_neighbor_back_to_front(cursor, cfra);
751 }
752 }
753
754 keylist->last_accessed_column = cursor;
755 return cursor;
756}
757
759 float cfra,
761 KeylistUpdateColumnFunction update_func,
762 void *userdata)
763{
765 !keylist->is_runtime_initialized,
766 "Modifying AnimKeylist isn't allowed after runtime is initialized "
767 "keylist->key_columns/columns_len will get out of sync with runtime.key_columns.");
768 if (ED_keylist_is_empty(keylist)) {
769 ActKeyColumn *key_column = create_func(userdata);
770 BLI_addhead(&keylist->key_columns, key_column);
771 keylist->column_len += 1;
772 keylist->last_accessed_column = key_column;
773 return;
774 }
775
776 ActKeyColumn *nearest = keylist_find_exact_or_neighbor_column(keylist, cfra);
777 if (is_cfra_eq(nearest->cfra, cfra)) {
778 update_func(nearest, userdata);
779 }
780 else if (is_cfra_lt(nearest->cfra, cfra)) {
781 ActKeyColumn *key_column = create_func(userdata);
782 BLI_insertlinkafter(&keylist->key_columns, nearest, key_column);
783 keylist->column_len += 1;
784 keylist->last_accessed_column = key_column;
785 }
786 else {
787 ActKeyColumn *key_column = create_func(userdata);
788 BLI_insertlinkbefore(&keylist->key_columns, nearest, key_column);
789 keylist->column_len += 1;
790 keylist->last_accessed_column = key_column;
791 }
792}
793
794/* Add the given BezTriple to the given 'list' of Keyframes */
796{
797 if (ELEM(nullptr, keylist, bezt)) {
798 return;
799 }
800
801 float cfra = bezt->cur->vec[1][0];
803}
804
805/* Add the given GPencil Frame to the given 'list' of Keyframes */
807{
808 if (ELEM(nullptr, keylist, gpf)) {
809 return;
810 }
811
812 float cfra = gpf->framenum;
814}
815
816/* Add the given MaskLayerShape Frame to the given 'list' of Keyframes */
817static void add_masklay_to_keycolumns_list(AnimKeylist *keylist, MaskLayerShape *masklay_shape)
818{
819 if (ELEM(nullptr, keylist, masklay_shape)) {
820 return;
821 }
822
823 float cfra = masklay_shape->frame;
825 keylist, cfra, nalloc_ak_masklayshape, nupdate_ak_masklayshape, masklay_shape);
826}
827
828/* ActKeyBlocks (Long Keyframes) ------------------------------------------ */
829
831
833 const BezTriple *prev,
834 const BezTriple *beztn)
835{
836 memset(info, 0, sizeof(ActKeyBlockInfo));
837
838 if (BEZKEYTYPE(beztn) == BEZT_KEYTYPE_MOVEHOLD) {
839 /* Animator tagged a "moving hold"
840 * - Previous key must also be tagged as a moving hold, otherwise
841 * we're just dealing with the first of a pair, and we don't
842 * want to be creating any phantom holds...
843 */
844 if (BEZKEYTYPE(prev) == BEZT_KEYTYPE_MOVEHOLD) {
846 }
847 }
848
849 /* Check for same values...
850 * - Handles must have same central value as each other
851 * - Handles which control that section of the curve must be constant
852 */
853 if (IS_EQF(beztn->vec[1][1], prev->vec[1][1])) {
854 bool hold;
855
856 /* Only check handles in case of actual bezier interpolation. */
857 if (prev->ipo == BEZT_IPO_BEZ) {
858 hold = IS_EQF(beztn->vec[1][1], beztn->vec[0][1]) &&
859 IS_EQF(prev->vec[1][1], prev->vec[2][1]);
860 }
861 /* This interpolation type induces movement even between identical columns. */
862 else {
863 hold = !ELEM(prev->ipo, BEZT_IPO_ELASTIC);
864 }
865
866 if (hold) {
868 }
869 }
870
871 /* Remember non-bezier interpolation info. */
872 if (prev->ipo != BEZT_IPO_BEZ) {
874 }
875
876 info->sel = BEZT_ISSEL_ANY(prev) || BEZT_ISSEL_ANY(beztn);
877}
878
880{
881 /* New curve and block. */
882 if (col->totcurve <= 1 && col->totblock == 0) {
883 memcpy(&col->block, block, sizeof(ActKeyBlockInfo));
884 }
885 /* Existing curve. */
886 else {
887 col->block.conflict |= (col->block.flag ^ block->flag);
888 col->block.flag |= block->flag;
889 col->block.sel |= block->sel;
890 }
891
892 if (block->flag) {
893 col->totblock++;
894 }
895}
896
897static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len)
898{
899 ActKeyColumn *col = static_cast<ActKeyColumn *>(keylist->key_columns.first);
900
901 if (bezt && bezt_len >= 2) {
902 ActKeyBlockInfo block;
903
904 /* Find the first key column while inserting dummy blocks. */
905 for (; col != nullptr && is_cfra_lt(col->cfra, bezt[0].vec[1][0]); col = col->next) {
907 }
908
909 BLI_assert(col != nullptr);
910
911 /* Insert real blocks. */
912 for (int v = 1; col != nullptr && v < bezt_len; v++, bezt++) {
913 /* Wrong order of bezier keys: resync position. */
914 if (is_cfra_lt(bezt[1].vec[1][0], bezt[0].vec[1][0])) {
915 /* Backtrack to find the right location. */
916 if (is_cfra_lt(bezt[1].vec[1][0], col->cfra)) {
918
919 BLI_assert(newcol);
920 BLI_assert(newcol->cfra == col->cfra);
921
922 col = newcol;
923 /* The previous keyblock is garbage too. */
924 if (col->prev != nullptr) {
926 }
927 }
928
929 continue;
930 }
931
932 /* In normal situations all keyframes are sorted. However, while keys are transformed, they
933 * may change order and then this assertion no longer holds. The effect is that the drawing
934 * isn't perfect during the transform; the "constant value" bars aren't updated until the
935 * transformation is confirmed. */
936 // BLI_assert(is_cfra_eq(col->cfra, bezt[0].vec[1][0]));
937
938 compute_keyblock_data(&block, bezt, bezt + 1);
939
940 for (; col != nullptr && is_cfra_lt(col->cfra, bezt[1].vec[1][0]); col = col->next) {
941 add_keyblock_info(col, &block);
942 }
943
944 BLI_assert(col != nullptr);
945 }
946 }
947
948 /* Insert dummy blocks at the end. */
949 for (; col != nullptr; col = col->next) {
951 }
952}
953
954/* Walk through columns and propagate blocks and totcurve.
955 *
956 * This must be called even by animation sources that don't generate
957 * keyblocks to keep the data structure consistent after adding columns.
958 */
959static void update_keyblocks(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len)
960{
961 /* Find the curve count. */
962 int max_curve = 0;
963
965 max_curve = std::max(max_curve, int(col->totcurve));
966 }
967
968 /* Propagate blocks to inserted keys. */
969 ActKeyColumn *prev_ready = nullptr;
970
972 /* Pre-existing column. */
973 if (col->totcurve > 0) {
974 prev_ready = col;
975 }
976 /* Newly inserted column, so copy block data from previous. */
977 else if (prev_ready != nullptr) {
978 col->totblock = prev_ready->totblock;
979 memcpy(&col->block, &prev_ready->block, sizeof(ActKeyBlockInfo));
980 }
981
982 col->totcurve = max_curve + 1;
983 }
984
985 /* Add blocks on top. */
986 add_bezt_to_keyblocks_list(keylist, bezt, bezt_len);
987}
988
989/* --------- */
990
992{
993 return ac != nullptr && ac->next != nullptr && ac->totblock > 0;
994}
995
997{
998 if (!actkeyblock_is_valid(ac)) {
999 return 0;
1000 }
1001
1003 return (ac->block.flag & ~ac->block.conflict) & hold_mask;
1004}
1005
1006/* *************************** Keyframe List Conversions *************************** */
1007
1009 AnimKeylist *keylist,
1010 const int saction_flag,
1011 blender::float2 range)
1012{
1013 if (!ac) {
1014 return;
1015 }
1016
1017 ListBase anim_data = {nullptr, nullptr};
1018
1019 /* Get F-Curves to take keyframes from. */
1021 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1022
1023 /* Loop through each F-Curve, grabbing the keyframes. */
1024 LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) {
1025 /* Why not use all #eAnim_KeyType here?
1026 * All of the other key types are actually "summaries" themselves,
1027 * and will just end up duplicating stuff that comes up through
1028 * standard filtering of just F-Curves. Given the way that these work,
1029 * there isn't really any benefit at all from including them. - Aligorith */
1030 switch (ale->datatype) {
1031 case ALE_FCURVE:
1032 fcurve_to_keylist(ale->adt,
1033 static_cast<FCurve *>(ale->data),
1034 keylist,
1035 saction_flag,
1036 range,
1038 break;
1039 case ALE_MASKLAY:
1040 mask_to_keylist(ac->ads, static_cast<MaskLayer *>(ale->data), keylist);
1041 break;
1042 case ALE_GPFRAME:
1043 gpl_to_keylist(ac->ads, static_cast<bGPDlayer *>(ale->data), keylist);
1044 break;
1047 ale->adt, static_cast<const GreasePencilLayer *>(ale->data), keylist, saction_flag);
1048 break;
1049 default:
1050 break;
1051 }
1052 }
1053
1054 ANIM_animdata_freelist(&anim_data);
1055}
1056
1058 ID *animated_id,
1059 animrig::Action &action,
1060 const animrig::slot_handle_t slot_handle,
1061 AnimKeylist *keylist,
1062 const int /* eSAction_Flag */ saction_flag,
1063 blender::float2 range)
1064{
1065 /* TODO: downstream code depends on this being non-null (see e.g.
1066 * `ANIM_animfilter_action_slot()` and `animfilter_fcurves_span()`). Either
1067 * change this parameter to be a reference, or modify the downstream code to
1068 * not assume that it's non-null and do something reasonable when it is null. */
1069 BLI_assert(animated_id);
1070
1071 if (!ac) {
1072 return;
1073 }
1074
1075 animrig::Slot *slot = action.slot_for_handle(slot_handle);
1076 if (!slot) {
1077 /* In the Dope Sheet mode of the Dope Sheet, an _Action_ channel actually shows the _Slot_ keys
1078 * in its summary line. When there are animated NLA control curves, that Action channel is also
1079 * shown, even when there is no slot assigned. So this function needs to be able to handle the
1080 * "no slot" case as valid. It just doesn't produce any keys.
1081 *
1082 * Also see `build_channel_keylist()` in `keyframes_draw.cc`. */
1083 return;
1084 }
1085
1086 ListBase anim_data = {nullptr, nullptr};
1087
1088 /* Get F-Curves to take keyframes from. */
1090 ANIM_animfilter_action_slot(ac, &anim_data, action, *slot, filter, animated_id);
1091
1092 LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) {
1093 /* As of the writing of this code, Actions ultimately only contain FCurves.
1094 * If/when that changes in the future, this may need to be updated. */
1095 if (ale->datatype != ALE_FCURVE) {
1096 continue;
1097 }
1098 fcurve_to_keylist(ale->adt,
1099 static_cast<FCurve *>(ale->data),
1100 keylist,
1101 saction_flag,
1102 range,
1104 }
1105
1106 ANIM_animdata_freelist(&anim_data);
1107}
1108
1110 Scene *sce,
1111 AnimKeylist *keylist,
1112 const int saction_flag,
1113 blender::float2 range)
1114{
1115 bAnimContext ac = {nullptr};
1116 ListBase anim_data = {nullptr, nullptr};
1117
1118 bAnimListElem dummy_chan = {nullptr};
1119
1120 if (sce == nullptr) {
1121 return;
1122 }
1123
1124 /* Create a dummy wrapper data to work with. */
1125 dummy_chan.type = ANIMTYPE_SCENE;
1126 dummy_chan.data = sce;
1127 dummy_chan.id = &sce->id;
1128 dummy_chan.adt = sce->adt;
1129
1130 ac.ads = ads;
1131 ac.data = &dummy_chan;
1135
1136 /* Get F-Curves to take keyframes from. */
1138
1139 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1140
1141 /* Loop through each F-Curve, grabbing the keyframes. */
1142 LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) {
1143 fcurve_to_keylist(ale->adt,
1144 static_cast<FCurve *>(ale->data),
1145 keylist,
1146 saction_flag,
1147 range,
1149 }
1150
1151 ANIM_animdata_freelist(&anim_data);
1152}
1153
1155 Object *ob,
1156 AnimKeylist *keylist,
1157 const int saction_flag,
1158 blender::float2 range)
1159{
1160 bAnimContext ac = {nullptr};
1161 ListBase anim_data = {nullptr, nullptr};
1162
1163 bAnimListElem dummy_chan = {nullptr};
1164 Base dummy_base = {nullptr};
1165
1166 if (ob == nullptr) {
1167 return;
1168 }
1169
1170 /* Create a dummy wrapper data to work with. */
1171 dummy_base.object = ob;
1172
1173 dummy_chan.type = ANIMTYPE_OBJECT;
1174 dummy_chan.data = &dummy_base;
1175 dummy_chan.id = &ob->id;
1176 dummy_chan.adt = ob->adt;
1177
1178 ac.ads = ads;
1179 ac.data = &dummy_chan;
1183
1184 /* Get F-Curves to take keyframes from. */
1186 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1187
1188 /* Loop through each F-Curve, grabbing the keyframes. */
1189 LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) {
1190 fcurve_to_keylist(ale->adt,
1191 static_cast<FCurve *>(ale->data),
1192 keylist,
1193 saction_flag,
1194 range,
1196 }
1197
1198 ANIM_animdata_freelist(&anim_data);
1199}
1200
1202 CacheFile *cache_file,
1203 AnimKeylist *keylist,
1204 const int saction_flag)
1205{
1206 if (cache_file == nullptr) {
1207 return;
1208 }
1209
1210 /* Create a dummy wrapper data to work with. */
1211 bAnimListElem dummy_chan = {nullptr};
1212 dummy_chan.type = ANIMTYPE_DSCACHEFILE;
1213 dummy_chan.data = cache_file;
1214 dummy_chan.id = &cache_file->id;
1215 dummy_chan.adt = cache_file->adt;
1216
1217 bAnimContext ac = {nullptr};
1218 ac.ads = ads;
1219 ac.data = &dummy_chan;
1223
1224 /* Get F-Curves to take keyframes from. */
1225 ListBase anim_data = {nullptr, nullptr};
1227 ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1228
1229 /* Loop through each F-Curve, grabbing the keyframes. */
1230 LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) {
1231 fcurve_to_keylist(ale->adt,
1232 static_cast<FCurve *>(ale->data),
1233 keylist,
1234 saction_flag,
1235 {-FLT_MAX, FLT_MAX},
1237 }
1238
1239 ANIM_animdata_freelist(&anim_data);
1240}
1241
1242static inline void set_up_beztriple_chain(BezTripleChain &chain,
1243 const FCurve *fcu,
1244 const int key_index,
1245 const bool do_extremes,
1246 const bool is_cyclic)
1247{
1248 chain.cur = &fcu->bezt[key_index];
1249
1250 /* Neighbor columns, accounting for being cyclic. */
1251 if (do_extremes) {
1252 chain.prev = (key_index > 0) ? &fcu->bezt[key_index - 1] :
1253 is_cyclic ? &fcu->bezt[fcu->totvert - 2] :
1254 nullptr;
1255 chain.next = (key_index + 1 < fcu->totvert) ? &fcu->bezt[key_index + 1] :
1256 is_cyclic ? &fcu->bezt[1] :
1257 nullptr;
1258 }
1259}
1260
1262 FCurve *fcu,
1263 AnimKeylist *keylist,
1264 const int saction_flag,
1265 blender::float2 range,
1266 const bool use_nla_remapping)
1267{
1268 /* This is not strictly necessary because `ANIM_nla_mapping_apply_fcurve()`
1269 * will just not do remapping if `adt` is null. Nevertheless, saying that you
1270 * want NLA remapping to be performed while not passing an `adt` (needed for
1271 * NLA remapping) almost certainly indicates a mistake somewhere. */
1272 BLI_assert_msg(!(use_nla_remapping && adt == nullptr),
1273 "Cannot perform NLA time remapping without an adt.");
1274
1275 if (!fcu || fcu->totvert == 0 || !fcu->bezt) {
1276 return;
1277 }
1279
1280 if (use_nla_remapping) {
1281 ANIM_nla_mapping_apply_fcurve(adt, fcu, false, false);
1282 }
1283
1284 const bool is_cyclic = BKE_fcurve_is_cyclic(fcu) && (fcu->totvert >= 2);
1285 const bool do_extremes = (saction_flag & SACTION_SHOW_EXTREMES) != 0;
1286
1287 BezTripleChain chain = {nullptr};
1288 /* The indices for which keys have been added to the key columns. Initialized as invalid bounds
1289 * for the case that no keyframes get added to the key-columns, which happens when the given
1290 * range doesn't overlap with the existing keyframes. */
1291 blender::Bounds<int> index_bounds(int(fcu->totvert), 0);
1292 /* The following is used to find the keys that are JUST outside the range. This is done so
1293 * drawing in the dope sheet can create lines that extend off-screen. */
1294 float left_outside_key_x = -FLT_MAX;
1295 float right_outside_key_x = FLT_MAX;
1296 int left_outside_key_index = -1;
1297 int right_outside_key_index = -1;
1298 /* Loop through beztriples, making ActKeysColumns. */
1299 for (int v = 0; v < fcu->totvert; v++) {
1300 /* Not using binary search to limit the range because the FCurve might not be sorted e.g. when
1301 * transforming in the Dope Sheet. */
1302 const float x = fcu->bezt[v].vec[1][0];
1303 if (x < range[0] && x > left_outside_key_x) {
1304 left_outside_key_x = x;
1305 left_outside_key_index = v;
1306 }
1307 if (x > range[1] && x < right_outside_key_x) {
1308 right_outside_key_x = x;
1309 right_outside_key_index = v;
1310 }
1311 if (x < range[0] || x > range[1]) {
1312 continue;
1313 }
1314 blender::math::min_max(v, index_bounds.min, index_bounds.max);
1315 set_up_beztriple_chain(chain, fcu, v, do_extremes, is_cyclic);
1316
1317 add_bezt_to_keycolumns_list(keylist, &chain);
1318 }
1319
1320 if (left_outside_key_index >= 0) {
1321 set_up_beztriple_chain(chain, fcu, left_outside_key_index, do_extremes, is_cyclic);
1322 add_bezt_to_keycolumns_list(keylist, &chain);
1323 /* Checking min and max because the FCurve might not be sorted. */
1324 index_bounds.min = blender::math::min(index_bounds.min, left_outside_key_index);
1325 index_bounds.max = blender::math::max(index_bounds.max, left_outside_key_index);
1326 }
1327 if (right_outside_key_index >= 0) {
1328 set_up_beztriple_chain(chain, fcu, right_outside_key_index, do_extremes, is_cyclic);
1329 add_bezt_to_keycolumns_list(keylist, &chain);
1330 index_bounds.min = blender::math::min(index_bounds.min, right_outside_key_index);
1331 index_bounds.max = blender::math::max(index_bounds.max, right_outside_key_index);
1332 }
1333 /* Not using index_bounds.is_empty() because that returns true if min and max are the same. That
1334 * is a valid configuration in this case though. */
1335 if (index_bounds.min <= index_bounds.max) {
1337 keylist, &fcu->bezt[index_bounds.min], (index_bounds.max + 1) - index_bounds.min);
1338 }
1339
1340 if (use_nla_remapping) {
1341 ANIM_nla_mapping_apply_fcurve(adt, fcu, true, false);
1342 }
1343}
1344
1346 bActionGroup *agrp,
1347 AnimKeylist *keylist,
1348 const int saction_flag,
1349 blender::float2 range)
1350{
1351 if (!agrp) {
1352 return;
1353 }
1354
1355 /* Legacy actions. */
1356 if (agrp->wrap().is_legacy()) {
1357 LISTBASE_FOREACH (FCurve *, fcu, &agrp->channels) {
1358 if (fcu->grp != agrp) {
1359 break;
1360 }
1361 fcurve_to_keylist(adt, fcu, keylist, saction_flag, range, true);
1362 }
1363 return;
1364 }
1365
1366 /* Layered actions. */
1367 animrig::Channelbag &channelbag = agrp->channelbag->wrap();
1368 Span<FCurve *> fcurves = channelbag.fcurves().slice(agrp->fcurve_range_start,
1369 agrp->fcurve_range_length);
1370 for (FCurve *fcurve : fcurves) {
1371 fcurve_to_keylist(adt, fcurve, keylist, saction_flag, range, true);
1372 }
1373}
1374
1376 bAction *dna_action,
1377 AnimKeylist *keylist,
1378 const int saction_flag,
1379 blender::float2 range)
1380{
1381 if (!dna_action) {
1382 return;
1383 }
1384
1385 blender::animrig::Action &action = dna_action->wrap();
1386
1387 /* TODO: move this into fcurves_for_action_slot(). */
1388 if (action.is_action_legacy()) {
1389 LISTBASE_FOREACH (FCurve *, fcu, &action.curves) {
1390 fcurve_to_keylist(adt, fcu, keylist, saction_flag, range, true);
1391 }
1392 return;
1393 }
1394
1399 BLI_assert(adt);
1400 for (FCurve *fcurve : fcurves_for_action_slot(action, adt->slot_handle)) {
1401 fcurve_to_keylist(adt, fcurve, keylist, saction_flag, range, true);
1402 }
1403}
1404
1405void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, AnimKeylist *keylist, const bool active)
1406{
1407 if (!gpd || !keylist) {
1408 return;
1409 }
1410
1411 /* For now, just aggregate out all the frames, but only for visible layers. */
1413 if (gpl->flag & GP_LAYER_HIDE) {
1414 continue;
1415 }
1416 if ((!active) || ((active) && (gpl->flag & GP_LAYER_SELECT))) {
1417 gpl_to_keylist(ads, gpl, keylist);
1418 }
1419 }
1420}
1421
1423 const GreasePencil *grease_pencil,
1424 AnimKeylist *keylist,
1425 const int saction_flag,
1426 const bool active_layer_only)
1427{
1428 if ((grease_pencil == nullptr) || (keylist == nullptr)) {
1429 return;
1430 }
1431
1432 if (active_layer_only && grease_pencil->has_active_layer()) {
1433 grease_pencil_cels_to_keylist(adt, grease_pencil->get_active_layer(), keylist, saction_flag);
1434 return;
1435 }
1436
1437 for (const blender::bke::greasepencil::Layer *layer : grease_pencil->layers()) {
1438 grease_pencil_cels_to_keylist(adt, layer, keylist, saction_flag);
1439 }
1440}
1441
1443 const GreasePencilLayer *gpl,
1444 AnimKeylist *keylist,
1445 int /*saction_flag*/)
1446{
1447 using namespace blender::bke::greasepencil;
1448 const Layer &layer = gpl->wrap();
1449 for (auto item : layer.frames().items()) {
1450 GreasePencilCel cel{};
1451 cel.frame_number = item.key;
1452 cel.frame = item.value;
1453
1454 float cfra = float(item.key);
1456 keylist, cfra, nalloc_ak_cel, nupdate_ak_cel, static_cast<void *>(&cel));
1457 }
1458}
1459
1461 const GreasePencilLayerTreeGroup *layer_group,
1462 AnimKeylist *keylist,
1463 const int saction_flag)
1464{
1465 if ((layer_group == nullptr) || (keylist == nullptr)) {
1466 return;
1467 }
1468
1470 const blender::bke::greasepencil::TreeNode &node = node_->wrap();
1471 if (node.is_group()) {
1472 grease_pencil_layer_group_to_keylist(adt, &node.as_group(), keylist, saction_flag);
1473 }
1474 else if (node.is_layer()) {
1475 grease_pencil_cels_to_keylist(adt, &node.as_layer(), keylist, saction_flag);
1476 }
1477 }
1478}
1479
1480void gpl_to_keylist(bDopeSheet * /*ads*/, bGPDlayer *gpl, AnimKeylist *keylist)
1481{
1482 if (!gpl || !keylist) {
1483 return;
1484 }
1485
1487 /* Although the frames should already be in an ordered list,
1488 * they are not suitable for displaying yet. */
1489 LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
1490 add_gpframe_to_keycolumns_list(keylist, gpf);
1491 }
1492
1493 update_keyblocks(keylist, nullptr, 0);
1494}
1495
1496void mask_to_keylist(bDopeSheet * /*ads*/, MaskLayer *masklay, AnimKeylist *keylist)
1497{
1498 if (!masklay || !keylist) {
1499 return;
1500 }
1502 LISTBASE_FOREACH (MaskLayerShape *, masklay_shape, &masklay->splines_shapes) {
1503 add_masklay_to_keycolumns_list(keylist, masklay_shape);
1504 }
1505
1506 update_keyblocks(keylist, nullptr, 0);
1507}
1508
1509void sequencer_strip_to_keylist(const Strip &strip, AnimKeylist &keylist, Scene &scene)
1510{
1511 if (!blender::seq::retiming_is_active(&strip)) {
1512 return;
1513 }
1515 for (const SeqRetimingKey &retime_key : blender::seq::retiming_keys_get(&strip)) {
1516 const float cfra = blender::seq::retiming_key_timeline_frame_get(&scene, &strip, &retime_key);
1517 SeqAllocateData allocate_data = {&retime_key, cfra};
1519 &keylist, cfra, nalloc_ak_seqframe, nupdate_ak_seqframe, &allocate_data);
1520 }
1521 update_keyblocks(&keylist, nullptr, 0);
1522}
Functions and classes to work with Actions.
bool BKE_fcurve_is_cyclic(const FCurve *fcu)
#define BEZT_BINARYSEARCH_THRESH
Low-level operations for grease pencil.
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define BLI_INLINE
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE void BLI_listbase_clear(ListBase *lb)
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
void BLI_insertlinkafter(ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:332
void BLI_addhead(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:91
void BLI_insertlinkbefore(ListBase *listbase, void *vnextlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:371
#define ELEM(...)
#define IS_EQF(a, b)
#define IS_EQT(a, b, c)
eDopeSheet_FilterFlag2
eDopeSheet_FilterFlag
@ SACTION_SHOW_EXTREMES
@ HD_AUTO_ANIM
@ HD_VECT
@ HD_FREE
@ HD_AUTO
@ BEZT_IPO_ELASTIC
@ BEZT_IPO_BEZ
eBezTriple_KeyframeType
@ BEZT_KEYTYPE_MOVEHOLD
@ BEZT_KEYTYPE_KEYFRAME
#define BEZT_ISSEL_ANY(bezt)
#define BEZKEYTYPE(bezt)
@ MASK_SHAPE_SELECT
Object is a sort of wrapper for general info.
@ SEQ_KEY_SELECTED
@ ANIMTYPE_SCENE
@ ANIMTYPE_DSCACHEFILE
@ ANIMTYPE_OBJECT
@ ALE_GREASE_PENCIL_CEL
@ ALE_GPFRAME
@ ALE_FCURVE
@ ALE_MASKLAY
@ ANIMCONT_CHANNEL
eAnimFilter_Flags
@ ANIMFILTER_DATA_VISIBLE
@ ANIMFILTER_FCURVESONLY
@ ACTKEYBLOCK_FLAG_ANY_HOLD
@ ACTKEYBLOCK_FLAG_MOVING_HOLD
@ ACTKEYBLOCK_FLAG_GPENCIL
@ ACTKEYBLOCK_FLAG_NON_BEZIER
@ ACTKEYBLOCK_FLAG_STATIC_HOLD
eKeyframeHandleDrawOpts
@ KEYFRAME_HANDLE_VECTOR
@ KEYFRAME_HANDLE_FREE
@ KEYFRAME_HANDLE_AUTO_CLAMP
@ KEYFRAME_HANDLE_AUTO
@ KEYFRAME_HANDLE_ALIGNED
eKeyframeExtremeDrawOpts
@ KEYFRAME_EXTREME_MAX
@ KEYFRAME_EXTREME_MIXED
@ KEYFRAME_EXTREME_NONE
@ KEYFRAME_EXTREME_FLAT
@ KEYFRAME_EXTREME_MIN
Read Guarded memory(de)allocation.
void ANIM_animdata_freelist(ListBase *anim_data)
Definition anim_deps.cc:463
void ANIM_nla_mapping_apply_fcurve(AnimData *adt, FCurve *fcu, bool restore, bool only_keys)
Definition anim_draw.cc:374
bool ANIM_nla_mapping_allowed(const bAnimListElem *ale)
Definition anim_draw.cc:274
size_t ANIM_animfilter_action_slot(bAnimContext *ac, ListBase *anim_data, animrig::Action &action, animrig::Slot &slot, const eAnimFilter_Flags filter_mode, ID *animated_id)
size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, const eAnimFilter_Flags filter_mode, void *data, const eAnimCont_Types datatype)
iter begin(iter)
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert * v
long long int int64_t
ItemIterator items() const &
Definition BLI_map.hh:902
Slot * slot_for_handle(slot_handle_t handle)
blender::Span< const FCurve * > fcurves() const
const Map< FramesMapKeyT, GreasePencilFrame > & frames() const
const LayerGroup & as_group() const
nullptr float
#define SELECT
static bool is_cyclic(const Nurb *nu)
uint col
#define active
#define filter
static ActKeyColumn * keylist_find_neighbor_back_to_front(ActKeyColumn *cursor, float cfra)
BLI_INLINE bool is_cfra_lt(const float a, const float b)
void mask_to_keylist(bDopeSheet *, MaskLayer *masklay, AnimKeylist *keylist)
BLI_INLINE bool is_cfra_eq(const float a, const float b)
static void keylist_convert_key_columns_to_array(AnimKeylist *keylist)
static ActKeyColumn * nalloc_ak_seqframe(void *data)
static void keylist_first_last(const AnimKeylist *keylist, const ActKeyColumn **first_column, const ActKeyColumn **last_column)
static void keylist_reset_last_accessed(AnimKeylist *keylist)
const ActKeyColumn * ED_keylist_find_prev(const AnimKeylist *keylist, const float cfra)
static eKeyframeHandleDrawOpts bezt_handle_type(const BezTriple *bezt)
static ActKeyColumn * nalloc_ak_cel(void *data)
bool actkeyblock_is_valid(const ActKeyColumn *ac)
static void nupdate_ak_gpframe(ActKeyColumn *ak, void *data)
void action_slot_summary_to_keylist(bAnimContext *ac, ID *animated_id, animrig::Action &action, const animrig::slot_handle_t slot_handle, AnimKeylist *keylist, const int saction_flag, blender::float2 range)
static ActKeyColumn * keylist_find_neighbor_front_to_back(ActKeyColumn *cursor, float cfra)
const ActKeyColumn * ED_keylist_find_any_between(const AnimKeylist *keylist, const Bounds< float > frame_range)
void fcurve_to_keylist(AnimData *adt, FCurve *fcu, AnimKeylist *keylist, const int saction_flag, blender::float2 range, const bool use_nla_remapping)
std::function< ActKeyColumn *(void *userdata)> KeylistCreateColumnFunction
std::function< void(ActKeyColumn *, void *)> KeylistUpdateColumnFunction
void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, AnimKeylist *keylist, const bool active)
static ActKeyColumn * nalloc_ak_masklayshape(void *data)
static void nupdate_ak_cel(ActKeyColumn *ak, void *data)
static void keylist_runtime_init(AnimKeylist *keylist)
static void nupdate_ak_masklayshape(ActKeyColumn *ak, void *data)
void scene_to_keylist(bDopeSheet *ads, Scene *sce, AnimKeylist *keylist, const int saction_flag, blender::float2 range)
static ActKeyColumn * nalloc_ak_gpframe(void *data)
static void add_gpframe_to_keycolumns_list(AnimKeylist *keylist, bGPDframe *gpf)
void ob_to_keylist(bDopeSheet *ads, Object *ob, AnimKeylist *keylist, const int saction_flag, blender::float2 range)
void cachefile_to_keylist(bDopeSheet *ads, CacheFile *cache_file, AnimKeylist *keylist, const int saction_flag)
void grease_pencil_data_block_to_keylist(AnimData *adt, const GreasePencil *grease_pencil, AnimKeylist *keylist, const int saction_flag, const bool active_layer_only)
static void nupdate_ak_bezt(ActKeyColumn *ak, void *data)
const ActKeyColumn * ED_keylist_find_closest(const AnimKeylist *keylist, const float cfra)
void ED_keylist_prepare_for_direct_access(AnimKeylist *keylist)
static void keylist_runtime_update_key_column_next_prev(AnimKeylist *keylist)
bool ED_keylist_all_keys_frame_range(const AnimKeylist *keylist, Bounds< float > *r_frame_range)
void summary_to_keylist(bAnimContext *ac, AnimKeylist *keylist, const int saction_flag, blender::float2 range)
static void keylist_runtime_init_listbase(AnimKeylist *keylist)
static const ActKeyColumn * keylist_find_upper_bound(const AnimKeylist *keylist, const float cfra)
static void compute_keyblock_data(ActKeyBlockInfo *info, const BezTriple *prev, const BezTriple *beztn)
static void add_keyblock_info(ActKeyColumn *col, const ActKeyBlockInfo *block)
AnimKeylist * ED_keylist_create()
static const ActKeyBlockInfo dummy_keyblock
int actkeyblock_get_valid_hold(const ActKeyColumn *ac)
static void set_up_beztriple_chain(BezTripleChain &chain, const FCurve *fcu, const int key_index, const bool do_extremes, const bool is_cyclic)
void ED_keylist_free(AnimKeylist *keylist)
static ActKeyColumn * keylist_find_exact_or_neighbor_column(AnimKeylist *keylist, float cfra)
void sequencer_strip_to_keylist(const Strip &strip, AnimKeylist &keylist, Scene &scene)
static eKeyframeExtremeDrawOpts bezt_extreme_type(const BezTripleChain *chain)
static void nupdate_ak_seqframe(ActKeyColumn *ak, void *data)
static ActKeyColumn * nalloc_ak_bezt(void *data)
static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len)
static void update_keyblocks(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len)
bool ED_keylist_selected_keys_frame_range(const AnimKeylist *keylist, Bounds< float > *r_frame_range)
static void add_masklay_to_keycolumns_list(AnimKeylist *keylist, MaskLayerShape *masklay_shape)
const ActKeyColumn * ED_keylist_find_next(const AnimKeylist *keylist, const float cfra)
static void keylist_add_or_update_column(AnimKeylist *keylist, float cfra, KeylistCreateColumnFunction create_func, KeylistUpdateColumnFunction update_func, void *userdata)
static const ActKeyColumn * keylist_find_lower_bound(const AnimKeylist *keylist, const float cfra)
int64_t ED_keylist_array_len(const AnimKeylist *keylist)
const ListBase * ED_keylist_listbase(const AnimKeylist *keylist)
bool ED_keylist_is_empty(const AnimKeylist *keylist)
const ActKeyColumn * ED_keylist_find_exact(const AnimKeylist *keylist, const float cfra)
void grease_pencil_layer_group_to_keylist(AnimData *adt, const GreasePencilLayerTreeGroup *layer_group, AnimKeylist *keylist, const int saction_flag)
static void add_bezt_to_keycolumns_list(AnimKeylist *keylist, BezTripleChain *bezt)
void action_to_keylist(AnimData *adt, bAction *dna_action, AnimKeylist *keylist, const int saction_flag, blender::float2 range)
void grease_pencil_cels_to_keylist(AnimData *, const GreasePencilLayer *gpl, AnimKeylist *keylist, int)
void gpl_to_keylist(bDopeSheet *, bGPDlayer *gpl, AnimKeylist *keylist)
void action_group_to_keylist(AnimData *adt, bActionGroup *agrp, AnimKeylist *keylist, const int saction_flag, blender::float2 range)
const ActKeyColumn * ED_keylist_array(const AnimKeylist *keylist)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
static ulong * next
decltype(::ActionSlot::handle) slot_handle_t
T min(const T &a, const T &b)
void min_max(const T &value, T &min, T &max)
T max(const T &a, const T &b)
int retiming_key_timeline_frame_get(const Scene *scene, const Strip *strip, const SeqRetimingKey *key)
MutableSpan< SeqRetimingKey > retiming_keys_get(const Strip *strip)
bool retiming_is_active(const Strip *strip)
VecBase< float, 2 > float2
static PyObject * create_func(PyObject *, PyObject *args)
Definition python.cpp:157
#define FLT_MAX
Definition stdcycles.h:14
eBezTriple_KeyframeType key_type
ActKeyColumn * prev
ActKeyColumn * next
ActKeyBlockInfo block
int32_t slot_handle
struct AnimKeylist::@177352003075216107275373003375205053134051274013 runtime
ListBase list_wrapper
std::optional< ActKeyColumn * > last_accessed_column
struct Object * object
float vec[3][3]
struct AnimData * adt
BezTriple * bezt
unsigned int totvert
GreasePencilFrame frame
Definition DNA_ID.h:414
void * last
void * first
ListBase splines_shapes
struct AnimData * adt
struct AnimData * adt
const SeqRetimingKey * key
struct ActionChannelbag * channelbag
ListBase curves
eDopeSheet_FilterFlag flag
eAnimCont_Types datatype
bDopeSheet * ads
struct bAnimContext::@242276144047124000362312251357014355232301013033 filters
eDopeSheet_FilterFlag2 flag2
AnimData * adt
eAnim_ChannelType type