Blender  V3.3
keyframes_keylist.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2009 Blender Foundation, Joshua Leung. All rights reserved. */
3 
8 /* System includes ----------------------------------------------------- */
9 
10 #include <algorithm>
11 #include <cfloat>
12 #include <cmath>
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_dlrbTree.h"
22 #include "BLI_listbase.h"
23 #include "BLI_math.h"
24 #include "BLI_range.h"
25 #include "BLI_utildefines.h"
26 
27 #include "DNA_anim_types.h"
28 #include "DNA_cachefile_types.h"
29 #include "DNA_gpencil_types.h"
30 #include "DNA_mask_types.h"
31 #include "DNA_object_types.h"
32 #include "DNA_scene_types.h"
33 
34 #include "BKE_fcurve.h"
35 
36 #include "ED_anim_api.h"
37 #include "ED_keyframes_keylist.h"
38 
39 extern "C" {
40 /* *************************** Keyframe Processing *************************** */
41 
42 /* ActKeyColumns (Keyframe Columns) ------------------------------------------ */
43 
44 BLI_INLINE bool is_cfra_eq(const float a, const float b)
45 {
47 }
48 
49 BLI_INLINE bool is_cfra_lt(const float a, const float b)
50 {
51  return (b - a) > BEZT_BINARYSEARCH_THRESH;
52 }
53 
54 /* --------------- */
55 
56 struct AnimKeylist {
57  /* Number of ActKeyColumn's in the keylist. */
58  size_t column_len = 0;
59 
60  bool is_runtime_initialized = false;
61 
62  /* Before initializing the runtime, the key_columns list base is used to quickly add columns.
63  * Contains `ActKeyColumn`. Should not be used after runtime is initialized. */
64  ListBase /* ActKeyColumn */ key_columns;
65  /* Last accessed column in the key_columns list base. Inserting columns are typically done in
66  * order. The last accessed column is used as starting point to search for a location to add or
67  * update the next column. */
68  std::optional<ActKeyColumn *> last_accessed_column = std::nullopt;
69 
70  struct {
71  /* When initializing the runtime the columns from the list base `AnimKeyList.key_columns` are
72  * transferred to an array to support binary searching and index based access. */
74  /* Wrapper around runtime.key_columns so it can still be accessed as a ListBase. Elements are
75  * owned by runtime.key_columns. */
76  ListBase /* ActKeyColumn */ list_wrapper;
78 
80  {
81  BLI_listbase_clear(&this->key_columns);
82  BLI_listbase_clear(&this->runtime.list_wrapper);
83  }
84 
86  {
87  BLI_freelistN(&this->key_columns);
88  BLI_listbase_clear(&this->runtime.list_wrapper);
89  }
90 
91 #ifdef WITH_CXX_GUARDEDALLOC
92  MEM_CXX_CLASS_ALLOC_FUNCS("editors:AnimKeylist")
93 #endif
94 };
95 
97 {
98  AnimKeylist *keylist = new AnimKeylist();
99  return keylist;
100 }
101 
103 {
104  BLI_assert(keylist);
105  delete keylist;
106 }
107 
109 {
110  size_t index;
111  LISTBASE_FOREACH_INDEX (ActKeyColumn *, key, &keylist->key_columns, index) {
112  keylist->runtime.key_columns[index] = *key;
113  }
114 }
115 
117 {
118  for (size_t index = 0; index < keylist->column_len; index++) {
119  const bool is_first = (index == 0);
120  keylist->runtime.key_columns[index].prev = is_first ? nullptr :
121  &keylist->runtime.key_columns[index - 1];
122  const bool is_last = (index == keylist->column_len - 1);
123  keylist->runtime.key_columns[index].next = is_last ? nullptr :
124  &keylist->runtime.key_columns[index + 1];
125  }
126 }
127 
129 {
130  if (ED_keylist_is_empty(keylist)) {
132  return;
133  }
134 
135  keylist->runtime.list_wrapper.first = keylist->runtime.key_columns.data();
136  keylist->runtime.list_wrapper.last = &keylist->runtime.key_columns[keylist->column_len - 1];
137 }
138 
140 {
142 
144 
145  /* Convert linked list to array to support fast searching. */
147  /* Ensure that the array can also be used as a listbase for external usages. */
150 
151  keylist->is_runtime_initialized = true;
152 }
153 
155 {
157  keylist->last_accessed_column.reset();
158 }
159 
161 {
162  if (keylist->is_runtime_initialized) {
163  return;
164  }
165  ED_keylist_runtime_init(keylist);
166 }
167 
169  const float cfra)
170 {
171  BLI_assert(!ED_keylist_is_empty(keylist));
172  const ActKeyColumn *begin = std::begin(keylist->runtime.key_columns);
173  const ActKeyColumn *end = std::end(keylist->runtime.key_columns);
174  ActKeyColumn value;
175  value.cfra = cfra;
176 
177  const ActKeyColumn *found_column = std::lower_bound(
178  begin, end, value, [](const ActKeyColumn &column, const ActKeyColumn &other) {
179  return is_cfra_lt(column.cfra, other.cfra);
180  });
181  return found_column;
182 }
183 
185  const float cfra)
186 {
187  BLI_assert(!ED_keylist_is_empty(keylist));
188  const ActKeyColumn *begin = std::begin(keylist->runtime.key_columns);
189  const ActKeyColumn *end = std::end(keylist->runtime.key_columns);
190  ActKeyColumn value;
191  value.cfra = cfra;
192 
193  const ActKeyColumn *found_column = std::upper_bound(
194  begin, end, value, [](const ActKeyColumn &column, const ActKeyColumn &other) {
195  return is_cfra_lt(column.cfra, other.cfra);
196  });
197  return found_column;
198 }
199 
200 const ActKeyColumn *ED_keylist_find_exact(const AnimKeylist *keylist, const float cfra)
201 {
203  "ED_keylist_prepare_for_direct_access needs to be called before searching.");
204 
205  if (ED_keylist_is_empty(keylist)) {
206  return nullptr;
207  }
208 
209  const ActKeyColumn *found_column = ED_keylist_find_lower_bound(keylist, cfra);
210 
211  const ActKeyColumn *end = std::end(keylist->runtime.key_columns);
212  if (found_column == end) {
213  return nullptr;
214  }
215  if (is_cfra_eq(found_column->cfra, cfra)) {
216  return found_column;
217  }
218  return nullptr;
219 }
220 
221 const ActKeyColumn *ED_keylist_find_next(const AnimKeylist *keylist, const float cfra)
222 {
224  "ED_keylist_prepare_for_direct_access needs to be called before searching.");
225 
226  if (ED_keylist_is_empty(keylist)) {
227  return nullptr;
228  }
229 
230  const ActKeyColumn *found_column = ED_keylist_find_upper_bound(keylist, cfra);
231 
232  const ActKeyColumn *end = std::end(keylist->runtime.key_columns);
233  if (found_column == end) {
234  return nullptr;
235  }
236  return found_column;
237 }
238 
239 const ActKeyColumn *ED_keylist_find_prev(const AnimKeylist *keylist, const float cfra)
240 {
242  "ED_keylist_prepare_for_direct_access needs to be called before searching.");
243 
244  if (ED_keylist_is_empty(keylist)) {
245  return nullptr;
246  }
247 
248  const ActKeyColumn *end = std::end(keylist->runtime.key_columns);
249  const ActKeyColumn *found_column = ED_keylist_find_lower_bound(keylist, cfra);
250 
251  if (found_column == end) {
252  /* Nothing found, return the last item. */
253  return end - 1;
254  }
255 
256  const ActKeyColumn *prev_column = found_column->prev;
257  return prev_column;
258 }
259 
261  const Range2f frame_range)
262 {
264  "ED_keylist_prepare_for_direct_access needs to be called before searching.");
265 
266  if (ED_keylist_is_empty(keylist)) {
267  return nullptr;
268  }
269 
270  const ActKeyColumn *column = ED_keylist_find_lower_bound(keylist, frame_range.min);
271  const ActKeyColumn *end = std::end(keylist->runtime.key_columns);
272  if (column == end) {
273  return nullptr;
274  }
275  if (column->cfra >= frame_range.max) {
276  return nullptr;
277  }
278  return column;
279 }
280 
281 const ActKeyColumn *ED_keylist_array(const struct AnimKeylist *keylist)
282 {
284  keylist->is_runtime_initialized,
285  "ED_keylist_prepare_for_direct_access needs to be called before accessing array.");
286  return keylist->runtime.key_columns.data();
287 }
288 
290 {
291  return keylist->column_len;
292 }
293 
294 bool ED_keylist_is_empty(const struct AnimKeylist *keylist)
295 {
296  return keylist->column_len == 0;
297 }
298 
299 const struct ListBase *ED_keylist_listbase(const AnimKeylist *keylist)
300 {
301  if (keylist->is_runtime_initialized) {
302  return &keylist->runtime.list_wrapper;
303  }
304  return &keylist->key_columns;
305 }
306 
307 static void keylist_first_last(const struct AnimKeylist *keylist,
308  const struct ActKeyColumn **first_column,
309  const struct ActKeyColumn **last_column)
310 {
311  if (keylist->is_runtime_initialized) {
312  *first_column = keylist->runtime.key_columns.data();
313  *last_column = &keylist->runtime.key_columns[keylist->column_len - 1];
314  }
315  else {
316  *first_column = static_cast<const ActKeyColumn *>(keylist->key_columns.first);
317  *last_column = static_cast<const ActKeyColumn *>(keylist->key_columns.last);
318  }
319 }
320 
321 bool ED_keylist_all_keys_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range)
322 {
323  BLI_assert(r_frame_range);
324 
325  if (ED_keylist_is_empty(keylist)) {
326  return false;
327  }
328 
329  const ActKeyColumn *first_column;
330  const ActKeyColumn *last_column;
331  keylist_first_last(keylist, &first_column, &last_column);
332  r_frame_range->min = first_column->cfra;
333  r_frame_range->max = last_column->cfra;
334 
335  return true;
336 }
337 
339  Range2f *r_frame_range)
340 {
341  BLI_assert(r_frame_range);
342 
343  if (ED_keylist_is_empty(keylist)) {
344  return false;
345  }
346 
347  const ActKeyColumn *first_column;
348  const ActKeyColumn *last_column;
349  keylist_first_last(keylist, &first_column, &last_column);
350  while (first_column && !(first_column->sel & SELECT)) {
351  first_column = first_column->next;
352  }
353  while (last_column && !(last_column->sel & SELECT)) {
354  last_column = last_column->prev;
355  }
356  if (!first_column || !last_column || first_column == last_column) {
357  return false;
358  }
359  r_frame_range->min = first_column->cfra;
360  r_frame_range->max = last_column->cfra;
361 
362  return true;
363 }
364 
365 /* Set of references to three logically adjacent keys. */
367  /* Current keyframe. */
369 
370  /* Logical neighbors. May be nullptr. */
372 };
373 
374 /* Categorize the interpolation & handle type of the keyframe. */
376 {
377  if (bezt->h1 == HD_AUTO_ANIM && bezt->h2 == HD_AUTO_ANIM) {
379  }
380  if (ELEM(bezt->h1, HD_AUTO_ANIM, HD_AUTO) && ELEM(bezt->h2, HD_AUTO_ANIM, HD_AUTO)) {
381  return KEYFRAME_HANDLE_AUTO;
382  }
383  if (bezt->h1 == HD_VECT && bezt->h2 == HD_VECT) {
384  return KEYFRAME_HANDLE_VECTOR;
385  }
386  if (ELEM(HD_FREE, bezt->h1, bezt->h2)) {
387  return KEYFRAME_HANDLE_FREE;
388  }
390 }
391 
392 /* Determine if the keyframe is an extreme by comparing with neighbors.
393  * Ends of fixed-value sections and of the whole curve are also marked.
394  */
396 {
397  if (chain->prev == nullptr && chain->next == nullptr) {
398  return KEYFRAME_EXTREME_NONE;
399  }
400 
401  /* Keyframe values for the current one and neighbors. */
402  const float cur_y = chain->cur->vec[1][1];
403  float prev_y = cur_y, next_y = cur_y;
404 
405  if (chain->prev && !IS_EQF(cur_y, chain->prev->vec[1][1])) {
406  prev_y = chain->prev->vec[1][1];
407  }
408  if (chain->next && !IS_EQF(cur_y, chain->next->vec[1][1])) {
409  next_y = chain->next->vec[1][1];
410  }
411 
412  /* Static hold. */
413  if (prev_y == cur_y && next_y == cur_y) {
414  return KEYFRAME_EXTREME_FLAT;
415  }
416 
417  /* Middle of an incline. */
418  if ((prev_y < cur_y && next_y > cur_y) || (prev_y > cur_y && next_y < cur_y)) {
419  return KEYFRAME_EXTREME_NONE;
420  }
421 
422  /* Bezier handle values for the overshoot check. */
423  const bool l_bezier = chain->prev && chain->prev->ipo == BEZT_IPO_BEZ;
424  const bool r_bezier = chain->next && chain->cur->ipo == BEZT_IPO_BEZ;
425  const float handle_l = l_bezier ? chain->cur->vec[0][1] : cur_y;
426  const float handle_r = r_bezier ? chain->cur->vec[2][1] : cur_y;
427 
428  /* Detect extremes. One of the neighbors is allowed to be equal to current. */
429  if (prev_y < cur_y || next_y < cur_y) {
430  const bool is_overshoot = (handle_l > cur_y || handle_r > cur_y);
431 
432  return static_cast<eKeyframeExtremeDrawOpts>(KEYFRAME_EXTREME_MAX |
433  (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0));
434  }
435 
436  if (prev_y > cur_y || next_y > cur_y) {
437  const bool is_overshoot = (handle_l < cur_y || handle_r < cur_y);
438 
439  return static_cast<eKeyframeExtremeDrawOpts>(KEYFRAME_EXTREME_MIN |
440  (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0));
441  }
442 
443  return KEYFRAME_EXTREME_NONE;
444 }
445 
446 /* New node callback used for building ActKeyColumns from BezTripleChain */
448 {
449  ActKeyColumn *ak = static_cast<ActKeyColumn *>(
450  MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn"));
451  const BezTripleChain *chain = static_cast<const BezTripleChain *>(data);
452  const BezTriple *bezt = chain->cur;
453 
454  /* store settings based on state of BezTriple */
455  ak->cfra = bezt->vec[1][0];
456  ak->sel = BEZT_ISSEL_ANY(bezt) ? SELECT : 0;
457  ak->key_type = BEZKEYTYPE(bezt);
458  ak->handle_type = bezt_handle_type(bezt);
459  ak->extreme_type = bezt_extreme_type(chain);
460 
461  /* count keyframes in this column */
462  ak->totkey = 1;
463 
464  return ak;
465 }
466 
467 /* Node updater callback used for building ActKeyColumns from BezTripleChain */
468 static void nupdate_ak_bezt(ActKeyColumn *ak, void *data)
469 {
470  const BezTripleChain *chain = static_cast<const BezTripleChain *>(data);
471  const BezTriple *bezt = chain->cur;
472 
473  /* set selection status and 'touched' status */
474  if (BEZT_ISSEL_ANY(bezt)) {
475  ak->sel = SELECT;
476  }
477 
478  /* count keyframes in this column */
479  ak->totkey++;
480 
481  /* For keyframe type, 'proper' keyframes have priority over breakdowns
482  * (and other types for now). */
483  if (BEZKEYTYPE(bezt) == BEZT_KEYTYPE_KEYFRAME) {
485  }
486 
487  /* For interpolation type, select the highest value (enum is sorted). */
489 
490  /* For extremes, detect when combining different states. */
491  const char new_extreme = bezt_extreme_type(chain);
492 
493  if (new_extreme != ak->extreme_type) {
494  /* Replace the flat status without adding mixed. */
495  if (ak->extreme_type == KEYFRAME_EXTREME_FLAT) {
496  ak->extreme_type = new_extreme;
497  }
498  else if (new_extreme != KEYFRAME_EXTREME_FLAT) {
499  ak->extreme_type |= (new_extreme | KEYFRAME_EXTREME_MIXED);
500  }
501  }
502 }
503 
504 /* ......... */
505 
506 /* New node callback used for building ActKeyColumns from GPencil frames */
508 {
509  ActKeyColumn *ak = static_cast<ActKeyColumn *>(
510  MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF"));
511  const bGPDframe *gpf = (bGPDframe *)data;
512 
513  /* store settings based on state of BezTriple */
514  ak->cfra = gpf->framenum;
515  ak->sel = (gpf->flag & GP_FRAME_SELECT) ? SELECT : 0;
516  ak->key_type = gpf->key_type;
517 
518  /* count keyframes in this column */
519  ak->totkey = 1;
520  /* Set as visible block. */
521  ak->totblock = 1;
522  ak->block.sel = ak->sel;
524 
525  return ak;
526 }
527 
528 /* Node updater callback used for building ActKeyColumns from GPencil frames */
529 static void nupdate_ak_gpframe(ActKeyColumn *ak, void *data)
530 {
531  bGPDframe *gpf = (bGPDframe *)data;
532 
533  /* set selection status and 'touched' status */
534  if (gpf->flag & GP_FRAME_SELECT) {
535  ak->sel = SELECT;
536  }
537 
538  /* count keyframes in this column */
539  ak->totkey++;
540 
541  /* for keyframe type, 'proper' keyframes have priority over breakdowns
542  * (and other types for now). */
543  if (gpf->key_type == BEZT_KEYTYPE_KEYFRAME) {
545  }
546 }
547 
548 /* ......... */
549 
550 /* New node callback used for building ActKeyColumns from GPencil frames */
552 {
553  ActKeyColumn *ak = static_cast<ActKeyColumn *>(
554  MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF"));
555  const MaskLayerShape *masklay_shape = (const MaskLayerShape *)data;
556 
557  /* store settings based on state of BezTriple */
558  ak->cfra = masklay_shape->frame;
559  ak->sel = (masklay_shape->flag & MASK_SHAPE_SELECT) ? SELECT : 0;
560 
561  /* count keyframes in this column */
562  ak->totkey = 1;
563 
564  return ak;
565 }
566 
567 /* Node updater callback used for building ActKeyColumns from GPencil frames */
569 {
570  MaskLayerShape *masklay_shape = (MaskLayerShape *)data;
571 
572  /* set selection status and 'touched' status */
573  if (masklay_shape->flag & MASK_SHAPE_SELECT) {
574  ak->sel = SELECT;
575  }
576 
577  /* count keyframes in this column */
578  ak->totkey++;
579 }
580 
581 /* --------------- */
582 using KeylistCreateColumnFunction = std::function<ActKeyColumn *(void *userdata)>;
583 using KeylistUpdateColumnFunction = std::function<void(ActKeyColumn *, void *)>;
584 
585 /* `ED_keylist_find_neighbor_front_to_back` is called before the runtime can be initialized so we
586  * cannot use bin searching. */
588 {
589  while (cursor->next && cursor->next->cfra <= cfra) {
590  cursor = cursor->next;
591  }
592  return cursor;
593 }
594 
595 /* `ED_keylist_find_neighbor_back_to_front` is called before the runtime can be initialized so we
596  * cannot use bin searching. */
598 {
599  while (cursor->prev && cursor->prev->cfra >= cfra) {
600  cursor = cursor->prev;
601  }
602  return cursor;
603 }
604 
605 /*
606  * `ED_keylist_find_exact_or_neighbor_column` is called before the runtime can be initialized so
607  * we cannot use bin searching.
608  *
609  * This function is called to add or update columns in the keylist.
610  * Typically columns are sorted by frame number so keeping track of the last_accessed_column
611  * reduces searching.
612  */
614 {
616  if (ED_keylist_is_empty(keylist)) {
617  return nullptr;
618  }
619 
620  ActKeyColumn *cursor = keylist->last_accessed_column.value_or(
621  static_cast<ActKeyColumn *>(keylist->key_columns.first));
622  if (!is_cfra_eq(cursor->cfra, cfra)) {
623  const bool walking_direction_front_to_back = cursor->cfra <= cfra;
624  if (walking_direction_front_to_back) {
625  cursor = ED_keylist_find_neighbor_front_to_back(cursor, cfra);
626  }
627  else {
628  cursor = ED_keylist_find_neighbor_back_to_front(cursor, cfra);
629  }
630  }
631 
632  keylist->last_accessed_column = cursor;
633  return cursor;
634 }
635 
637  float cfra,
639  KeylistUpdateColumnFunction update_func,
640  void *userdata)
641 {
643  !keylist->is_runtime_initialized,
644  "Modifying AnimKeylist isn't allowed after runtime is initialized "
645  "keylist->key_columns/columns_len will get out of sync with runtime.key_columns.");
646  if (ED_keylist_is_empty(keylist)) {
647  ActKeyColumn *key_column = create_func(userdata);
648  BLI_addhead(&keylist->key_columns, key_column);
649  keylist->column_len += 1;
650  keylist->last_accessed_column = key_column;
651  return;
652  }
653 
654  ActKeyColumn *nearest = ED_keylist_find_exact_or_neighbor_column(keylist, cfra);
655  if (is_cfra_eq(nearest->cfra, cfra)) {
656  update_func(nearest, userdata);
657  }
658  else if (is_cfra_lt(nearest->cfra, cfra)) {
659  ActKeyColumn *key_column = create_func(userdata);
660  BLI_insertlinkafter(&keylist->key_columns, nearest, key_column);
661  keylist->column_len += 1;
662  keylist->last_accessed_column = key_column;
663  }
664  else {
665  ActKeyColumn *key_column = create_func(userdata);
666  BLI_insertlinkbefore(&keylist->key_columns, nearest, key_column);
667  keylist->column_len += 1;
668  keylist->last_accessed_column = key_column;
669  }
670 }
671 
672 /* Add the given BezTriple to the given 'list' of Keyframes */
674 {
675  if (ELEM(nullptr, keylist, bezt)) {
676  return;
677  }
678 
679  float cfra = bezt->cur->vec[1][0];
681 }
682 
683 /* Add the given GPencil Frame to the given 'list' of Keyframes */
685 {
686  if (ELEM(nullptr, keylist, gpf)) {
687  return;
688  }
689 
690  float cfra = gpf->framenum;
692 }
693 
694 /* Add the given MaskLayerShape Frame to the given 'list' of Keyframes */
695 static void add_masklay_to_keycolumns_list(AnimKeylist *keylist, MaskLayerShape *masklay_shape)
696 {
697  if (ELEM(nullptr, keylist, masklay_shape)) {
698  return;
699  }
700 
701  float cfra = masklay_shape->frame;
703  keylist, cfra, nalloc_ak_masklayshape, nupdate_ak_masklayshape, masklay_shape);
704 }
705 
706 /* ActKeyBlocks (Long Keyframes) ------------------------------------------ */
707 
708 static const ActKeyBlockInfo dummy_keyblock = {0};
709 
711  const BezTriple *prev,
712  const BezTriple *beztn)
713 {
714  memset(info, 0, sizeof(ActKeyBlockInfo));
715 
716  if (BEZKEYTYPE(beztn) == BEZT_KEYTYPE_MOVEHOLD) {
717  /* Animator tagged a "moving hold"
718  * - Previous key must also be tagged as a moving hold, otherwise
719  * we're just dealing with the first of a pair, and we don't
720  * want to be creating any phantom holds...
721  */
724  }
725  }
726 
727  /* Check for same values...
728  * - Handles must have same central value as each other
729  * - Handles which control that section of the curve must be constant
730  */
731  if (IS_EQF(beztn->vec[1][1], prev->vec[1][1])) {
732  bool hold;
733 
734  /* Only check handles in case of actual bezier interpolation. */
735  if (prev->ipo == BEZT_IPO_BEZ) {
736  hold = IS_EQF(beztn->vec[1][1], beztn->vec[0][1]) &&
737  IS_EQF(prev->vec[1][1], prev->vec[2][1]);
738  }
739  /* This interpolation type induces movement even between identical columns. */
740  else {
741  hold = !ELEM(prev->ipo, BEZT_IPO_ELASTIC);
742  }
743 
744  if (hold) {
746  }
747  }
748 
749  /* Remember non-bezier interpolation info. */
750  if (prev->ipo != BEZT_IPO_BEZ) {
752  }
753 
754  info->sel = BEZT_ISSEL_ANY(prev) || BEZT_ISSEL_ANY(beztn);
755 }
756 
758 {
759  /* New curve and block. */
760  if (col->totcurve <= 1 && col->totblock == 0) {
761  memcpy(&col->block, block, sizeof(ActKeyBlockInfo));
762  }
763  /* Existing curve. */
764  else {
765  col->block.conflict |= (col->block.flag ^ block->flag);
766  col->block.flag |= block->flag;
767  col->block.sel |= block->sel;
768  }
769 
770  if (block->flag) {
771  col->totblock++;
772  }
773 }
774 
775 static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len)
776 {
777  ActKeyColumn *col = static_cast<ActKeyColumn *>(keylist->key_columns.first);
778 
779  if (bezt && bezt_len >= 2) {
780  ActKeyBlockInfo block;
781 
782  /* Find the first key column while inserting dummy blocks. */
783  for (; col != nullptr && is_cfra_lt(col->cfra, bezt[0].vec[1][0]); col = col->next) {
785  }
786 
787  BLI_assert(col != nullptr);
788 
789  /* Insert real blocks. */
790  for (int v = 1; col != nullptr && v < bezt_len; v++, bezt++) {
791  /* Wrong order of bezier keys: resync position. */
792  if (is_cfra_lt(bezt[1].vec[1][0], bezt[0].vec[1][0])) {
793  /* Backtrack to find the right location. */
794  if (is_cfra_lt(bezt[1].vec[1][0], col->cfra)) {
796 
797  BLI_assert(newcol);
798  BLI_assert(newcol->cfra == col->cfra);
799 
800  col = newcol;
801  /* The previous keyblock is garbage too. */
802  if (col->prev != nullptr) {
804  }
805  }
806 
807  continue;
808  }
809 
810  /* In normal situations all keyframes are sorted. However, while keys are transformed, they
811  * may change order and then this assertion no longer holds. The effect is that the drawing
812  * isn't perfect during the transform; the "constant value" bars aren't updated until the
813  * transformation is confirmed. */
814  // BLI_assert(is_cfra_eq(col->cfra, bezt[0].vec[1][0]));
815 
816  compute_keyblock_data(&block, bezt, bezt + 1);
817 
818  for (; col != nullptr && is_cfra_lt(col->cfra, bezt[1].vec[1][0]); col = col->next) {
819  add_keyblock_info(col, &block);
820  }
821 
822  BLI_assert(col != nullptr);
823  }
824  }
825 
826  /* Insert dummy blocks at the end. */
827  for (; col != nullptr; col = col->next) {
829  }
830 }
831 
832 /* Walk through columns and propagate blocks and totcurve.
833  *
834  * This must be called even by animation sources that don't generate
835  * keyblocks to keep the data structure consistent after adding columns.
836  */
837 static void update_keyblocks(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len)
838 {
839  /* Find the curve count */
840  int max_curve = 0;
841 
842  LISTBASE_FOREACH (ActKeyColumn *, col, &keylist->key_columns) {
843  max_curve = MAX2(max_curve, col->totcurve);
844  }
845 
846  /* Propagate blocks to inserted keys */
847  ActKeyColumn *prev_ready = nullptr;
848 
849  LISTBASE_FOREACH (ActKeyColumn *, col, &keylist->key_columns) {
850  /* Pre-existing column. */
851  if (col->totcurve > 0) {
852  prev_ready = col;
853  }
854  /* Newly inserted column, so copy block data from previous. */
855  else if (prev_ready != nullptr) {
856  col->totblock = prev_ready->totblock;
857  memcpy(&col->block, &prev_ready->block, sizeof(ActKeyBlockInfo));
858  }
859 
860  col->totcurve = max_curve + 1;
861  }
862 
863  /* Add blocks on top */
864  add_bezt_to_keyblocks_list(keylist, bezt, bezt_len);
865 }
866 
867 /* --------- */
868 
870 {
871  return ac != nullptr && ac->next != nullptr && ac->totblock > 0;
872 }
873 
875 {
876  /* check that block is valid */
877  if (!actkeyblock_is_valid(ac)) {
878  return 0;
879  }
880 
882  return (ac->block.flag & ~ac->block.conflict) & hold_mask;
883 }
884 
885 /* *************************** Keyframe List Conversions *************************** */
886 
887 void summary_to_keylist(bAnimContext *ac, AnimKeylist *keylist, const int saction_flag)
888 {
889  if (ac) {
890  ListBase anim_data = {nullptr, nullptr};
891 
892  /* get F-Curves to take keyframes from */
895  ac, &anim_data, filter, ac->data, static_cast<eAnimCont_Types>(ac->datatype));
896 
897  /* loop through each F-Curve, grabbing the keyframes */
898  LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) {
899  /* Why not use all #eAnim_KeyType here?
900  * All of the other key types are actually "summaries" themselves,
901  * and will just end up duplicating stuff that comes up through
902  * standard filtering of just F-Curves. Given the way that these work,
903  * there isn't really any benefit at all from including them. - Aligorith */
904  switch (ale->datatype) {
905  case ALE_FCURVE:
906  fcurve_to_keylist(ale->adt, static_cast<FCurve *>(ale->data), keylist, saction_flag);
907  break;
908  case ALE_MASKLAY:
909  mask_to_keylist(ac->ads, static_cast<MaskLayer *>(ale->data), keylist);
910  break;
911  case ALE_GPFRAME:
912  gpl_to_keylist(ac->ads, static_cast<bGPDlayer *>(ale->data), keylist);
913  break;
914  default:
915  // printf("%s: datatype %d unhandled\n", __func__, ale->datatype);
916  break;
917  }
918  }
919 
920  ANIM_animdata_freelist(&anim_data);
921  }
922 }
923 
924 void scene_to_keylist(bDopeSheet *ads, Scene *sce, AnimKeylist *keylist, const int saction_flag)
925 {
926  bAnimContext ac = {nullptr};
927  ListBase anim_data = {nullptr, nullptr};
928 
929  bAnimListElem dummychan = {nullptr};
930 
931  if (sce == nullptr) {
932  return;
933  }
934 
935  /* create a dummy wrapper data to work with */
936  dummychan.type = ANIMTYPE_SCENE;
937  dummychan.data = sce;
938  dummychan.id = &sce->id;
939  dummychan.adt = sce->adt;
940 
941  ac.ads = ads;
942  ac.data = &dummychan;
944 
945  /* get F-Curves to take keyframes from */
947 
949  &ac, &anim_data, filter, ac.data, static_cast<eAnimCont_Types>(ac.datatype));
950 
951  /* loop through each F-Curve, grabbing the keyframes */
952  LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) {
953  fcurve_to_keylist(ale->adt, static_cast<FCurve *>(ale->data), keylist, saction_flag);
954  }
955 
956  ANIM_animdata_freelist(&anim_data);
957 }
958 
959 void ob_to_keylist(bDopeSheet *ads, Object *ob, AnimKeylist *keylist, const int saction_flag)
960 {
961  bAnimContext ac = {nullptr};
962  ListBase anim_data = {nullptr, nullptr};
963 
964  bAnimListElem dummychan = {nullptr};
965  Base dummybase = {nullptr};
966 
967  if (ob == nullptr) {
968  return;
969  }
970 
971  /* create a dummy wrapper data to work with */
972  dummybase.object = ob;
973 
974  dummychan.type = ANIMTYPE_OBJECT;
975  dummychan.data = &dummybase;
976  dummychan.id = &ob->id;
977  dummychan.adt = ob->adt;
978 
979  ac.ads = ads;
980  ac.data = &dummychan;
982 
983  /* get F-Curves to take keyframes from */
986  &ac, &anim_data, filter, ac.data, static_cast<eAnimCont_Types>(ac.datatype));
987 
988  /* loop through each F-Curve, grabbing the keyframes */
989  LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) {
990  fcurve_to_keylist(ale->adt, static_cast<FCurve *>(ale->data), keylist, saction_flag);
991  }
992 
993  ANIM_animdata_freelist(&anim_data);
994 }
995 
997  CacheFile *cache_file,
998  AnimKeylist *keylist,
999  const int saction_flag)
1000 {
1001  if (cache_file == nullptr) {
1002  return;
1003  }
1004 
1005  /* create a dummy wrapper data to work with */
1006  bAnimListElem dummychan = {nullptr};
1007  dummychan.type = ANIMTYPE_DSCACHEFILE;
1008  dummychan.data = cache_file;
1009  dummychan.id = &cache_file->id;
1010  dummychan.adt = cache_file->adt;
1011 
1012  bAnimContext ac = {nullptr};
1013  ac.ads = ads;
1014  ac.data = &dummychan;
1016 
1017  /* get F-Curves to take keyframes from */
1018  ListBase anim_data = {nullptr, nullptr};
1021  &ac, &anim_data, filter, ac.data, static_cast<eAnimCont_Types>(ac.datatype));
1022 
1023  /* loop through each F-Curve, grabbing the keyframes */
1024  LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) {
1025  fcurve_to_keylist(ale->adt, static_cast<FCurve *>(ale->data), keylist, saction_flag);
1026  }
1027 
1028  ANIM_animdata_freelist(&anim_data);
1029 }
1030 
1031 void fcurve_to_keylist(AnimData *adt, FCurve *fcu, AnimKeylist *keylist, const int saction_flag)
1032 {
1033  if (fcu && fcu->totvert && fcu->bezt) {
1035  /* apply NLA-mapping (if applicable) */
1036  if (adt) {
1037  ANIM_nla_mapping_apply_fcurve(adt, fcu, false, false);
1038  }
1039 
1040  /* Check if the curve is cyclic. */
1041  bool is_cyclic = BKE_fcurve_is_cyclic(fcu) && (fcu->totvert >= 2);
1042  bool do_extremes = (saction_flag & SACTION_SHOW_EXTREMES) != 0;
1043 
1044  /* loop through beztriples, making ActKeysColumns */
1045  BezTripleChain chain = {nullptr};
1046 
1047  for (int v = 0; v < fcu->totvert; v++) {
1048  chain.cur = &fcu->bezt[v];
1049 
1050  /* Neighbor columns, accounting for being cyclic. */
1051  if (do_extremes) {
1052  chain.prev = (v > 0) ? &fcu->bezt[v - 1] :
1053  is_cyclic ? &fcu->bezt[fcu->totvert - 2] :
1054  nullptr;
1055  chain.next = (v + 1 < fcu->totvert) ? &fcu->bezt[v + 1] :
1056  is_cyclic ? &fcu->bezt[1] :
1057  nullptr;
1058  }
1059 
1060  add_bezt_to_keycolumns_list(keylist, &chain);
1061  }
1062 
1063  /* Update keyblocks. */
1064  update_keyblocks(keylist, fcu->bezt, fcu->totvert);
1065 
1066  /* unapply NLA-mapping if applicable */
1067  if (adt) {
1068  ANIM_nla_mapping_apply_fcurve(adt, fcu, true, false);
1069  }
1070  }
1071 }
1072 
1074  bActionGroup *agrp,
1075  AnimKeylist *keylist,
1076  const int saction_flag)
1077 {
1078  if (agrp) {
1079  /* loop through F-Curves */
1080  LISTBASE_FOREACH (FCurve *, fcu, &agrp->channels) {
1081  if (fcu->grp != agrp) {
1082  break;
1083  }
1084  fcurve_to_keylist(adt, fcu, keylist, saction_flag);
1085  }
1086  }
1087 }
1088 
1089 void action_to_keylist(AnimData *adt, bAction *act, AnimKeylist *keylist, const int saction_flag)
1090 {
1091  if (act) {
1092  /* loop through F-Curves */
1093  LISTBASE_FOREACH (FCurve *, fcu, &act->curves) {
1094  fcurve_to_keylist(adt, fcu, keylist, saction_flag);
1095  }
1096  }
1097 }
1098 
1099 void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, AnimKeylist *keylist, const bool active)
1100 {
1101  if (gpd && keylist) {
1102  /* for now, just aggregate out all the frames, but only for visible layers */
1103  LISTBASE_FOREACH_BACKWARD (bGPDlayer *, gpl, &gpd->layers) {
1104  if ((gpl->flag & GP_LAYER_HIDE) == 0) {
1105  if ((!active) || ((active) && (gpl->flag & GP_LAYER_SELECT))) {
1106  gpl_to_keylist(ads, gpl, keylist);
1107  }
1108  }
1109  }
1110  }
1111 }
1112 
1114 {
1115  if (gpl && keylist) {
1117  /* Although the frames should already be in an ordered list,
1118  * they are not suitable for displaying yet. */
1119  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
1120  add_gpframe_to_keycolumns_list(keylist, gpf);
1121  }
1122 
1123  update_keyblocks(keylist, nullptr, 0);
1124  }
1125 }
1126 
1127 void mask_to_keylist(bDopeSheet *UNUSED(ads), MaskLayer *masklay, AnimKeylist *keylist)
1128 {
1129  if (masklay && keylist) {
1131  LISTBASE_FOREACH (MaskLayerShape *, masklay_shape, &masklay->splines_shapes) {
1132  add_masklay_to_keycolumns_list(keylist, masklay_shape);
1133  }
1134 
1135  update_keyblocks(keylist, nullptr, 0);
1136  }
1137 }
1138 }
#define BEZT_BINARYSEARCH_THRESH
Definition: BKE_fcurve.h:222
bool BKE_fcurve_is_cyclic(struct FCurve *fcu)
Definition: fcurve.c:1192
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition: BLI_assert.h:53
#define BLI_INLINE
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:60
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
Definition: BLI_listbase.h:348
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
Definition: BLI_listbase.h:273
void BLI_insertlinkafter(struct ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition: listbase.c:301
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
Definition: BLI_listbase.h:344
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition: listbase.c:466
void BLI_insertlinkbefore(struct ListBase *listbase, void *vnextlink, void *vnewlink) ATTR_NONNULL(1)
Definition: listbase.c:340
#define UNUSED(x)
#define MAX2(a, b)
#define ELEM(...)
#define IS_EQF(a, b)
#define IS_EQT(a, b, c)
@ SACTION_SHOW_EXTREMES
#define BEZT_ISSEL_ANY(bezt)
@ HD_AUTO_ANIM
@ HD_VECT
@ HD_FREE
@ HD_AUTO
@ BEZT_IPO_ELASTIC
@ BEZT_IPO_BEZ
@ BEZT_KEYTYPE_MOVEHOLD
@ BEZT_KEYTYPE_KEYFRAME
@ GP_LAYER_SELECT
@ GP_LAYER_HIDE
@ GP_FRAME_SELECT
@ MASK_SHAPE_SELECT
Object is a sort of wrapper for general info.
@ ANIMTYPE_SCENE
Definition: ED_anim_api.h:196
@ ANIMTYPE_DSCACHEFILE
Definition: ED_anim_api.h:210
@ ANIMTYPE_OBJECT
Definition: ED_anim_api.h:197
@ ALE_GPFRAME
Definition: ED_anim_api.h:251
@ ALE_FCURVE
Definition: ED_anim_api.h:250
@ ALE_MASKLAY
Definition: ED_anim_api.h:252
eAnimCont_Types
Definition: ED_anim_api.h:102
@ ANIMCONT_CHANNEL
Definition: ED_anim_api.h:111
#define BEZKEYTYPE(bezt)
Definition: ED_anim_api.h:969
eAnimFilter_Flags
Definition: ED_anim_api.h:284
@ ANIMFILTER_DATA_VISIBLE
Definition: ED_anim_api.h:292
@ ANIMFILTER_FCURVESONLY
Definition: ED_anim_api.h:328
@ 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.c:397
void ANIM_nla_mapping_apply_fcurve(AnimData *adt, FCurve *fcu, bool restore, bool only_keys)
Definition: anim_draw.c:291
size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, eAnimFilter_Flags filter_mode, void *data, eAnimCont_Types datatype)
Definition: anim_filter.c:3447
ATTR_WARN_UNUSED_RESULT const BMVert * v
#define SELECT
SyclQueue void void size_t num_bytes void
static bool is_cyclic(const Nurb *nu)
uint col
DO_INLINE void filter(lfVector *V, fmatrix3x3 *S)
static ActKeyColumn * nalloc_ak_masklayshape(void *data)
BLI_INLINE bool is_cfra_lt(const float a, const float b)
void fcurve_to_keylist(AnimData *adt, FCurve *fcu, AnimKeylist *keylist, const int saction_flag)
BLI_INLINE bool is_cfra_eq(const float a, const float b)
AnimKeylist * ED_keylist_create()
void scene_to_keylist(bDopeSheet *ads, Scene *sce, AnimKeylist *keylist, const int saction_flag)
static ActKeyColumn * ED_keylist_find_exact_or_neighbor_column(AnimKeylist *keylist, float cfra)
void mask_to_keylist(bDopeSheet *UNUSED(ads), MaskLayer *masklay, AnimKeylist *keylist)
static eKeyframeHandleDrawOpts bezt_handle_type(const BezTriple *bezt)
bool actkeyblock_is_valid(const ActKeyColumn *ac)
static void nupdate_ak_gpframe(ActKeyColumn *ak, void *data)
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_gpframe(void *data)
static void ED_keylist_reset_last_accessed(AnimKeylist *keylist)
const ActKeyColumn * ED_keylist_find_any_between(const AnimKeylist *keylist, const Range2f frame_range)
static const ActKeyColumn * ED_keylist_find_upper_bound(const AnimKeylist *keylist, const float cfra)
void ob_to_keylist(bDopeSheet *ads, Object *ob, AnimKeylist *keylist, const int saction_flag)
static void keylist_first_last(const struct AnimKeylist *keylist, const struct ActKeyColumn **first_column, const struct ActKeyColumn **last_column)
static void nupdate_ak_masklayshape(ActKeyColumn *ak, void *data)
static void ED_keylist_runtime_init_listbase(AnimKeylist *keylist)
static void add_gpframe_to_keycolumns_list(AnimKeylist *keylist, bGPDframe *gpf)
const ActKeyColumn * ED_keylist_array(const struct AnimKeylist *keylist)
static void ED_keylist_runtime_init(AnimKeylist *keylist)
bool ED_keylist_is_empty(const struct AnimKeylist *keylist)
void cachefile_to_keylist(bDopeSheet *ads, CacheFile *cache_file, AnimKeylist *keylist, const int saction_flag)
static ActKeyColumn * ED_keylist_find_neighbor_back_to_front(ActKeyColumn *cursor, float cfra)
bool ED_keylist_selected_keys_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range)
static void nupdate_ak_bezt(ActKeyColumn *ak, void *data)
void summary_to_keylist(bAnimContext *ac, AnimKeylist *keylist, const int saction_flag)
void ED_keylist_prepare_for_direct_access(AnimKeylist *keylist)
const ActKeyColumn * ED_keylist_find_next(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)
static const ActKeyBlockInfo dummy_keyblock
static ActKeyColumn * ED_keylist_find_neighbor_front_to_back(ActKeyColumn *cursor, float cfra)
int actkeyblock_get_valid_hold(const ActKeyColumn *ac)
void ED_keylist_free(AnimKeylist *keylist)
static ActKeyColumn * nalloc_ak_bezt(void *data)
bool ED_keylist_all_keys_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range)
static const ActKeyColumn * ED_keylist_find_lower_bound(const AnimKeylist *keylist, const float cfra)
int64_t ED_keylist_array_len(const struct AnimKeylist *keylist)
void action_to_keylist(AnimData *adt, bAction *act, AnimKeylist *keylist, const int saction_flag)
static eKeyframeExtremeDrawOpts bezt_extreme_type(const BezTripleChain *chain)
static void ED_keylist_convert_key_columns_to_array(AnimKeylist *keylist)
static void ED_keylist_add_or_update_column(AnimKeylist *keylist, float cfra, KeylistCreateColumnFunction create_func, KeylistUpdateColumnFunction update_func, void *userdata)
static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len)
const struct ListBase * ED_keylist_listbase(const AnimKeylist *keylist)
static void update_keyblocks(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len)
void gpl_to_keylist(bDopeSheet *UNUSED(ads), bGPDlayer *gpl, AnimKeylist *keylist)
static void add_masklay_to_keycolumns_list(AnimKeylist *keylist, MaskLayerShape *masklay_shape)
void agroup_to_keylist(AnimData *adt, bActionGroup *agrp, AnimKeylist *keylist, const int saction_flag)
static void ED_keylist_runtime_update_key_column_next_prev(AnimKeylist *keylist)
const ActKeyColumn * ED_keylist_find_prev(const AnimKeylist *keylist, const float cfra)
static void add_bezt_to_keycolumns_list(AnimKeylist *keylist, BezTripleChain *bezt)
const ActKeyColumn * ED_keylist_find_exact(const AnimKeylist *keylist, const float cfra)
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
static unsigned a[3]
Definition: RandGen.cpp:78
bool active
all scheduled work for the GPU.
SymEdge< T > * prev(const SymEdge< T > *se)
Definition: delaunay_2d.cc:105
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
static PyObject * create_func(PyObject *, PyObject *args)
Definition: python.cpp:156
__int64 int64_t
Definition: stdint.h:89
ActKeyBlockInfo block
struct ActKeyColumn * next
struct ActKeyColumn * prev
ListBase list_wrapper
ListBase key_columns
struct AnimKeylist::@320 runtime
bool is_runtime_initialized
std::optional< ActKeyColumn * > last_accessed_column
blender::Array< ActKeyColumn > key_columns
struct Object * object
uint8_t h1
float vec[3][3]
uint8_t h2
struct AnimData * adt
BezTriple * bezt
unsigned int totvert
void * last
Definition: DNA_listBase.h:31
void * first
Definition: DNA_listBase.h:31
ListBase splines_shapes
struct AnimData * adt
float min
Definition: BLI_range.h:14
float max
Definition: BLI_range.h:15
struct AnimData * adt
ListBase curves
struct bDopeSheet * ads
Definition: ED_anim_api.h:79
short datatype
Definition: ED_anim_api.h:62
void * data
Definition: ED_anim_api.h:60
struct AnimData * adt
Definition: ED_anim_api.h:162
struct ID * id
Definition: ED_anim_api.h:160
ListBase frames
ListBase layers