This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/editors/animation/keyframes_keylist.cc
Falk David df0c2693b6 Refactor: Rename grease pencil files to legacy
This renames the `BKE_gpencil_*` as well as the `DNA_gpencil_types.h`
files to indicate that it's the legacy grease pencil.

Pull Request: blender/blender#105597
2023-03-13 10:42:51 +01:00

1139 lines
35 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2009 Blender Foundation, Joshua Leung. All rights reserved. */
/** \file
* \ingroup edanimation
*/
/* System includes ----------------------------------------------------- */
#include <algorithm>
#include <cfloat>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <optional>
#include "MEM_guardedalloc.h"
#include "BLI_array.hh"
#include "BLI_dlrbTree.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_range.h"
#include "BLI_utildefines.h"
#include "DNA_anim_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_gpencil_legacy_types.h"
#include "DNA_mask_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BKE_fcurve.h"
#include "ED_anim_api.h"
#include "ED_keyframes_keylist.h"
extern "C" {
/* *************************** Keyframe Processing *************************** */
/* ActKeyColumns (Keyframe Columns) ------------------------------------------ */
BLI_INLINE bool is_cfra_eq(const float a, const float b)
{
return IS_EQT(a, b, BEZT_BINARYSEARCH_THRESH);
}
BLI_INLINE bool is_cfra_lt(const float a, const float b)
{
return (b - a) > BEZT_BINARYSEARCH_THRESH;
}
/* --------------- */
struct AnimKeylist {
/* Number of ActKeyColumn's in the keylist. */
size_t column_len = 0;
bool is_runtime_initialized = false;
/* Before initializing the runtime, the key_columns list base is used to quickly add columns.
* Contains `ActKeyColumn`. Should not be used after runtime is initialized. */
ListBase /*ActKeyColumn*/ key_columns;
/* Last accessed column in the key_columns list base. Inserting columns are typically done in
* order. The last accessed column is used as starting point to search for a location to add or
* update the next column. */
std::optional<ActKeyColumn *> last_accessed_column = std::nullopt;
struct {
/* When initializing the runtime the columns from the list base `AnimKeyList.key_columns` are
* transferred to an array to support binary searching and index based access. */
blender::Array<ActKeyColumn> key_columns;
/* Wrapper around runtime.key_columns so it can still be accessed as a ListBase.
* Elements are owned by `runtime.key_columns`. */
ListBase /*ActKeyColumn*/ list_wrapper;
} runtime;
AnimKeylist()
{
BLI_listbase_clear(&this->key_columns);
BLI_listbase_clear(&this->runtime.list_wrapper);
}
~AnimKeylist()
{
BLI_freelistN(&this->key_columns);
BLI_listbase_clear(&this->runtime.list_wrapper);
}
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("editors:AnimKeylist")
#endif
};
AnimKeylist *ED_keylist_create()
{
AnimKeylist *keylist = new AnimKeylist();
return keylist;
}
void ED_keylist_free(AnimKeylist *keylist)
{
BLI_assert(keylist);
delete keylist;
}
static void ED_keylist_convert_key_columns_to_array(AnimKeylist *keylist)
{
size_t index;
LISTBASE_FOREACH_INDEX (ActKeyColumn *, key, &keylist->key_columns, index) {
keylist->runtime.key_columns[index] = *key;
}
}
static void ED_keylist_runtime_update_key_column_next_prev(AnimKeylist *keylist)
{
for (size_t index = 0; index < keylist->column_len; index++) {
const bool is_first = (index == 0);
keylist->runtime.key_columns[index].prev = is_first ? nullptr :
&keylist->runtime.key_columns[index - 1];
const bool is_last = (index == keylist->column_len - 1);
keylist->runtime.key_columns[index].next = is_last ? nullptr :
&keylist->runtime.key_columns[index + 1];
}
}
static void ED_keylist_runtime_init_listbase(AnimKeylist *keylist)
{
if (ED_keylist_is_empty(keylist)) {
BLI_listbase_clear(&keylist->runtime.list_wrapper);
return;
}
keylist->runtime.list_wrapper.first = keylist->runtime.key_columns.data();
keylist->runtime.list_wrapper.last = &keylist->runtime.key_columns[keylist->column_len - 1];
}
static void ED_keylist_runtime_init(AnimKeylist *keylist)
{
BLI_assert(!keylist->is_runtime_initialized);
keylist->runtime.key_columns = blender::Array<ActKeyColumn>(keylist->column_len);
/* Convert linked list to array to support fast searching. */
ED_keylist_convert_key_columns_to_array(keylist);
/* Ensure that the array can also be used as a listbase for external usages. */
ED_keylist_runtime_update_key_column_next_prev(keylist);
ED_keylist_runtime_init_listbase(keylist);
keylist->is_runtime_initialized = true;
}
static void ED_keylist_reset_last_accessed(AnimKeylist *keylist)
{
BLI_assert(!keylist->is_runtime_initialized);
keylist->last_accessed_column.reset();
}
void ED_keylist_prepare_for_direct_access(AnimKeylist *keylist)
{
if (keylist->is_runtime_initialized) {
return;
}
ED_keylist_runtime_init(keylist);
}
static const ActKeyColumn *ED_keylist_find_lower_bound(const AnimKeylist *keylist,
const float cfra)
{
BLI_assert(!ED_keylist_is_empty(keylist));
const ActKeyColumn *begin = std::begin(keylist->runtime.key_columns);
const ActKeyColumn *end = std::end(keylist->runtime.key_columns);
ActKeyColumn value;
value.cfra = cfra;
const ActKeyColumn *found_column = std::lower_bound(
begin, end, value, [](const ActKeyColumn &column, const ActKeyColumn &other) {
return is_cfra_lt(column.cfra, other.cfra);
});
return found_column;
}
static const ActKeyColumn *ED_keylist_find_upper_bound(const AnimKeylist *keylist,
const float cfra)
{
BLI_assert(!ED_keylist_is_empty(keylist));
const ActKeyColumn *begin = std::begin(keylist->runtime.key_columns);
const ActKeyColumn *end = std::end(keylist->runtime.key_columns);
ActKeyColumn value;
value.cfra = cfra;
const ActKeyColumn *found_column = std::upper_bound(
begin, end, value, [](const ActKeyColumn &column, const ActKeyColumn &other) {
return is_cfra_lt(column.cfra, other.cfra);
});
return found_column;
}
const ActKeyColumn *ED_keylist_find_exact(const AnimKeylist *keylist, const float cfra)
{
BLI_assert_msg(keylist->is_runtime_initialized,
"ED_keylist_prepare_for_direct_access needs to be called before searching.");
if (ED_keylist_is_empty(keylist)) {
return nullptr;
}
const ActKeyColumn *found_column = ED_keylist_find_lower_bound(keylist, cfra);
const ActKeyColumn *end = std::end(keylist->runtime.key_columns);
if (found_column == end) {
return nullptr;
}
if (is_cfra_eq(found_column->cfra, cfra)) {
return found_column;
}
return nullptr;
}
const ActKeyColumn *ED_keylist_find_next(const AnimKeylist *keylist, const float cfra)
{
BLI_assert_msg(keylist->is_runtime_initialized,
"ED_keylist_prepare_for_direct_access needs to be called before searching.");
if (ED_keylist_is_empty(keylist)) {
return nullptr;
}
const ActKeyColumn *found_column = ED_keylist_find_upper_bound(keylist, cfra);
const ActKeyColumn *end = std::end(keylist->runtime.key_columns);
if (found_column == end) {
return nullptr;
}
return found_column;
}
const ActKeyColumn *ED_keylist_find_prev(const AnimKeylist *keylist, const float cfra)
{
BLI_assert_msg(keylist->is_runtime_initialized,
"ED_keylist_prepare_for_direct_access needs to be called before searching.");
if (ED_keylist_is_empty(keylist)) {
return nullptr;
}
const ActKeyColumn *end = std::end(keylist->runtime.key_columns);
const ActKeyColumn *found_column = ED_keylist_find_lower_bound(keylist, cfra);
if (found_column == end) {
/* Nothing found, return the last item. */
return end - 1;
}
const ActKeyColumn *prev_column = found_column->prev;
return prev_column;
}
const ActKeyColumn *ED_keylist_find_any_between(const AnimKeylist *keylist,
const Range2f frame_range)
{
BLI_assert_msg(keylist->is_runtime_initialized,
"ED_keylist_prepare_for_direct_access needs to be called before searching.");
if (ED_keylist_is_empty(keylist)) {
return nullptr;
}
const ActKeyColumn *column = ED_keylist_find_lower_bound(keylist, frame_range.min);
const ActKeyColumn *end = std::end(keylist->runtime.key_columns);
if (column == end) {
return nullptr;
}
if (column->cfra >= frame_range.max) {
return nullptr;
}
return column;
}
const ActKeyColumn *ED_keylist_array(const struct AnimKeylist *keylist)
{
BLI_assert_msg(
keylist->is_runtime_initialized,
"ED_keylist_prepare_for_direct_access needs to be called before accessing array.");
return keylist->runtime.key_columns.data();
}
int64_t ED_keylist_array_len(const struct AnimKeylist *keylist)
{
return keylist->column_len;
}
bool ED_keylist_is_empty(const struct AnimKeylist *keylist)
{
return keylist->column_len == 0;
}
const struct ListBase *ED_keylist_listbase(const AnimKeylist *keylist)
{
if (keylist->is_runtime_initialized) {
return &keylist->runtime.list_wrapper;
}
return &keylist->key_columns;
}
static void keylist_first_last(const struct AnimKeylist *keylist,
const struct ActKeyColumn **first_column,
const struct ActKeyColumn **last_column)
{
if (keylist->is_runtime_initialized) {
*first_column = keylist->runtime.key_columns.data();
*last_column = &keylist->runtime.key_columns[keylist->column_len - 1];
}
else {
*first_column = static_cast<const ActKeyColumn *>(keylist->key_columns.first);
*last_column = static_cast<const ActKeyColumn *>(keylist->key_columns.last);
}
}
bool ED_keylist_all_keys_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range)
{
BLI_assert(r_frame_range);
if (ED_keylist_is_empty(keylist)) {
return false;
}
const ActKeyColumn *first_column;
const ActKeyColumn *last_column;
keylist_first_last(keylist, &first_column, &last_column);
r_frame_range->min = first_column->cfra;
r_frame_range->max = last_column->cfra;
return true;
}
bool ED_keylist_selected_keys_frame_range(const struct AnimKeylist *keylist,
Range2f *r_frame_range)
{
BLI_assert(r_frame_range);
if (ED_keylist_is_empty(keylist)) {
return false;
}
const ActKeyColumn *first_column;
const ActKeyColumn *last_column;
keylist_first_last(keylist, &first_column, &last_column);
while (first_column && !(first_column->sel & SELECT)) {
first_column = first_column->next;
}
while (last_column && !(last_column->sel & SELECT)) {
last_column = last_column->prev;
}
if (!first_column || !last_column || first_column == last_column) {
return false;
}
r_frame_range->min = first_column->cfra;
r_frame_range->max = last_column->cfra;
return true;
}
/* Set of references to three logically adjacent keys. */
struct BezTripleChain {
/* Current keyframe. */
BezTriple *cur;
/* Logical neighbors. May be nullptr. */
BezTriple *prev, *next;
};
/* Categorize the interpolation & handle type of the keyframe. */
static eKeyframeHandleDrawOpts bezt_handle_type(const BezTriple *bezt)
{
if (bezt->h1 == HD_AUTO_ANIM && bezt->h2 == HD_AUTO_ANIM) {
return KEYFRAME_HANDLE_AUTO_CLAMP;
}
if (ELEM(bezt->h1, HD_AUTO_ANIM, HD_AUTO) && ELEM(bezt->h2, HD_AUTO_ANIM, HD_AUTO)) {
return KEYFRAME_HANDLE_AUTO;
}
if (bezt->h1 == HD_VECT && bezt->h2 == HD_VECT) {
return KEYFRAME_HANDLE_VECTOR;
}
if (ELEM(HD_FREE, bezt->h1, bezt->h2)) {
return KEYFRAME_HANDLE_FREE;
}
return KEYFRAME_HANDLE_ALIGNED;
}
/* Determine if the keyframe is an extreme by comparing with neighbors.
* Ends of fixed-value sections and of the whole curve are also marked.
*/
static eKeyframeExtremeDrawOpts bezt_extreme_type(const BezTripleChain *chain)
{
if (chain->prev == nullptr && chain->next == nullptr) {
return KEYFRAME_EXTREME_NONE;
}
/* Keyframe values for the current one and neighbors. */
const float cur_y = chain->cur->vec[1][1];
float prev_y = cur_y, next_y = cur_y;
if (chain->prev && !IS_EQF(cur_y, chain->prev->vec[1][1])) {
prev_y = chain->prev->vec[1][1];
}
if (chain->next && !IS_EQF(cur_y, chain->next->vec[1][1])) {
next_y = chain->next->vec[1][1];
}
/* Static hold. */
if (prev_y == cur_y && next_y == cur_y) {
return KEYFRAME_EXTREME_FLAT;
}
/* Middle of an incline. */
if ((prev_y < cur_y && next_y > cur_y) || (prev_y > cur_y && next_y < cur_y)) {
return KEYFRAME_EXTREME_NONE;
}
/* Bezier handle values for the overshoot check. */
const bool l_bezier = chain->prev && chain->prev->ipo == BEZT_IPO_BEZ;
const bool r_bezier = chain->next && chain->cur->ipo == BEZT_IPO_BEZ;
const float handle_l = l_bezier ? chain->cur->vec[0][1] : cur_y;
const float handle_r = r_bezier ? chain->cur->vec[2][1] : cur_y;
/* Detect extremes. One of the neighbors is allowed to be equal to current. */
if (prev_y < cur_y || next_y < cur_y) {
const bool is_overshoot = (handle_l > cur_y || handle_r > cur_y);
return static_cast<eKeyframeExtremeDrawOpts>(KEYFRAME_EXTREME_MAX |
(is_overshoot ? KEYFRAME_EXTREME_MIXED : 0));
}
if (prev_y > cur_y || next_y > cur_y) {
const bool is_overshoot = (handle_l < cur_y || handle_r < cur_y);
return static_cast<eKeyframeExtremeDrawOpts>(KEYFRAME_EXTREME_MIN |
(is_overshoot ? KEYFRAME_EXTREME_MIXED : 0));
}
return KEYFRAME_EXTREME_NONE;
}
/* New node callback used for building ActKeyColumns from BezTripleChain */
static ActKeyColumn *nalloc_ak_bezt(void *data)
{
ActKeyColumn *ak = static_cast<ActKeyColumn *>(
MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn"));
const BezTripleChain *chain = static_cast<const BezTripleChain *>(data);
const BezTriple *bezt = chain->cur;
/* store settings based on state of BezTriple */
ak->cfra = bezt->vec[1][0];
ak->sel = BEZT_ISSEL_ANY(bezt) ? SELECT : 0;
ak->key_type = BEZKEYTYPE(bezt);
ak->handle_type = bezt_handle_type(bezt);
ak->extreme_type = bezt_extreme_type(chain);
/* count keyframes in this column */
ak->totkey = 1;
return ak;
}
/* Node updater callback used for building ActKeyColumns from BezTripleChain */
static void nupdate_ak_bezt(ActKeyColumn *ak, void *data)
{
const BezTripleChain *chain = static_cast<const BezTripleChain *>(data);
const BezTriple *bezt = chain->cur;
/* set selection status and 'touched' status */
if (BEZT_ISSEL_ANY(bezt)) {
ak->sel = SELECT;
}
/* count keyframes in this column */
ak->totkey++;
/* For keyframe type, 'proper' keyframes have priority over breakdowns
* (and other types for now). */
if (BEZKEYTYPE(bezt) == BEZT_KEYTYPE_KEYFRAME) {
ak->key_type = BEZT_KEYTYPE_KEYFRAME;
}
/* For interpolation type, select the highest value (enum is sorted). */
ak->handle_type = MAX2((eKeyframeHandleDrawOpts)ak->handle_type, bezt_handle_type(bezt));
/* For extremes, detect when combining different states. */
const char new_extreme = bezt_extreme_type(chain);
if (new_extreme != ak->extreme_type) {
/* Replace the flat status without adding mixed. */
if (ak->extreme_type == KEYFRAME_EXTREME_FLAT) {
ak->extreme_type = new_extreme;
}
else if (new_extreme != KEYFRAME_EXTREME_FLAT) {
ak->extreme_type |= (new_extreme | KEYFRAME_EXTREME_MIXED);
}
}
}
/* ......... */
/* New node callback used for building ActKeyColumns from GPencil frames */
static ActKeyColumn *nalloc_ak_gpframe(void *data)
{
ActKeyColumn *ak = static_cast<ActKeyColumn *>(
MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF"));
const bGPDframe *gpf = (bGPDframe *)data;
/* store settings based on state of BezTriple */
ak->cfra = gpf->framenum;
ak->sel = (gpf->flag & GP_FRAME_SELECT) ? SELECT : 0;
ak->key_type = gpf->key_type;
/* count keyframes in this column */
ak->totkey = 1;
/* Set as visible block. */
ak->totblock = 1;
ak->block.sel = ak->sel;
ak->block.flag |= ACTKEYBLOCK_FLAG_GPENCIL;
return ak;
}
/* Node updater callback used for building ActKeyColumns from GPencil frames */
static void nupdate_ak_gpframe(ActKeyColumn *ak, void *data)
{
bGPDframe *gpf = (bGPDframe *)data;
/* set selection status and 'touched' status */
if (gpf->flag & GP_FRAME_SELECT) {
ak->sel = SELECT;
}
/* count keyframes in this column */
ak->totkey++;
/* for keyframe type, 'proper' keyframes have priority over breakdowns
* (and other types for now). */
if (gpf->key_type == BEZT_KEYTYPE_KEYFRAME) {
ak->key_type = BEZT_KEYTYPE_KEYFRAME;
}
}
/* ......... */
/* New node callback used for building ActKeyColumns from GPencil frames */
static ActKeyColumn *nalloc_ak_masklayshape(void *data)
{
ActKeyColumn *ak = static_cast<ActKeyColumn *>(
MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF"));
const MaskLayerShape *masklay_shape = (const MaskLayerShape *)data;
/* store settings based on state of BezTriple */
ak->cfra = masklay_shape->frame;
ak->sel = (masklay_shape->flag & MASK_SHAPE_SELECT) ? SELECT : 0;
/* count keyframes in this column */
ak->totkey = 1;
return ak;
}
/* Node updater callback used for building ActKeyColumns from GPencil frames */
static void nupdate_ak_masklayshape(ActKeyColumn *ak, void *data)
{
MaskLayerShape *masklay_shape = (MaskLayerShape *)data;
/* set selection status and 'touched' status */
if (masklay_shape->flag & MASK_SHAPE_SELECT) {
ak->sel = SELECT;
}
/* count keyframes in this column */
ak->totkey++;
}
/* --------------- */
using KeylistCreateColumnFunction = std::function<ActKeyColumn *(void *userdata)>;
using KeylistUpdateColumnFunction = std::function<void(ActKeyColumn *, void *)>;
/* `ED_keylist_find_neighbor_front_to_back` is called before the runtime can be initialized so we
* cannot use bin searching. */
static ActKeyColumn *ED_keylist_find_neighbor_front_to_back(ActKeyColumn *cursor, float cfra)
{
while (cursor->next && cursor->next->cfra <= cfra) {
cursor = cursor->next;
}
return cursor;
}
/* `ED_keylist_find_neighbor_back_to_front` is called before the runtime can be initialized so we
* cannot use bin searching. */
static ActKeyColumn *ED_keylist_find_neighbor_back_to_front(ActKeyColumn *cursor, float cfra)
{
while (cursor->prev && cursor->prev->cfra >= cfra) {
cursor = cursor->prev;
}
return cursor;
}
/*
* `ED_keylist_find_exact_or_neighbor_column` is called before the runtime can be initialized so
* we cannot use bin searching.
*
* This function is called to add or update columns in the keylist.
* Typically columns are sorted by frame number so keeping track of the last_accessed_column
* reduces searching.
*/
static ActKeyColumn *ED_keylist_find_exact_or_neighbor_column(AnimKeylist *keylist, float cfra)
{
BLI_assert(!keylist->is_runtime_initialized);
if (ED_keylist_is_empty(keylist)) {
return nullptr;
}
ActKeyColumn *cursor = keylist->last_accessed_column.value_or(
static_cast<ActKeyColumn *>(keylist->key_columns.first));
if (!is_cfra_eq(cursor->cfra, cfra)) {
const bool walking_direction_front_to_back = cursor->cfra <= cfra;
if (walking_direction_front_to_back) {
cursor = ED_keylist_find_neighbor_front_to_back(cursor, cfra);
}
else {
cursor = ED_keylist_find_neighbor_back_to_front(cursor, cfra);
}
}
keylist->last_accessed_column = cursor;
return cursor;
}
static void ED_keylist_add_or_update_column(AnimKeylist *keylist,
float cfra,
KeylistCreateColumnFunction create_func,
KeylistUpdateColumnFunction update_func,
void *userdata)
{
BLI_assert_msg(
!keylist->is_runtime_initialized,
"Modifying AnimKeylist isn't allowed after runtime is initialized "
"keylist->key_columns/columns_len will get out of sync with runtime.key_columns.");
if (ED_keylist_is_empty(keylist)) {
ActKeyColumn *key_column = create_func(userdata);
BLI_addhead(&keylist->key_columns, key_column);
keylist->column_len += 1;
keylist->last_accessed_column = key_column;
return;
}
ActKeyColumn *nearest = ED_keylist_find_exact_or_neighbor_column(keylist, cfra);
if (is_cfra_eq(nearest->cfra, cfra)) {
update_func(nearest, userdata);
}
else if (is_cfra_lt(nearest->cfra, cfra)) {
ActKeyColumn *key_column = create_func(userdata);
BLI_insertlinkafter(&keylist->key_columns, nearest, key_column);
keylist->column_len += 1;
keylist->last_accessed_column = key_column;
}
else {
ActKeyColumn *key_column = create_func(userdata);
BLI_insertlinkbefore(&keylist->key_columns, nearest, key_column);
keylist->column_len += 1;
keylist->last_accessed_column = key_column;
}
}
/* Add the given BezTriple to the given 'list' of Keyframes */
static void add_bezt_to_keycolumns_list(AnimKeylist *keylist, BezTripleChain *bezt)
{
if (ELEM(nullptr, keylist, bezt)) {
return;
}
float cfra = bezt->cur->vec[1][0];
ED_keylist_add_or_update_column(keylist, cfra, nalloc_ak_bezt, nupdate_ak_bezt, bezt);
}
/* Add the given GPencil Frame to the given 'list' of Keyframes */
static void add_gpframe_to_keycolumns_list(AnimKeylist *keylist, bGPDframe *gpf)
{
if (ELEM(nullptr, keylist, gpf)) {
return;
}
float cfra = gpf->framenum;
ED_keylist_add_or_update_column(keylist, cfra, nalloc_ak_gpframe, nupdate_ak_gpframe, gpf);
}
/* Add the given MaskLayerShape Frame to the given 'list' of Keyframes */
static void add_masklay_to_keycolumns_list(AnimKeylist *keylist, MaskLayerShape *masklay_shape)
{
if (ELEM(nullptr, keylist, masklay_shape)) {
return;
}
float cfra = masklay_shape->frame;
ED_keylist_add_or_update_column(
keylist, cfra, nalloc_ak_masklayshape, nupdate_ak_masklayshape, masklay_shape);
}
/* ActKeyBlocks (Long Keyframes) ------------------------------------------ */
static const ActKeyBlockInfo dummy_keyblock = {0};
static void compute_keyblock_data(ActKeyBlockInfo *info,
const BezTriple *prev,
const BezTriple *beztn)
{
memset(info, 0, sizeof(ActKeyBlockInfo));
if (BEZKEYTYPE(beztn) == BEZT_KEYTYPE_MOVEHOLD) {
/* Animator tagged a "moving hold"
* - Previous key must also be tagged as a moving hold, otherwise
* we're just dealing with the first of a pair, and we don't
* want to be creating any phantom holds...
*/
if (BEZKEYTYPE(prev) == BEZT_KEYTYPE_MOVEHOLD) {
info->flag |= ACTKEYBLOCK_FLAG_MOVING_HOLD | ACTKEYBLOCK_FLAG_ANY_HOLD;
}
}
/* Check for same values...
* - Handles must have same central value as each other
* - Handles which control that section of the curve must be constant
*/
if (IS_EQF(beztn->vec[1][1], prev->vec[1][1])) {
bool hold;
/* Only check handles in case of actual bezier interpolation. */
if (prev->ipo == BEZT_IPO_BEZ) {
hold = IS_EQF(beztn->vec[1][1], beztn->vec[0][1]) &&
IS_EQF(prev->vec[1][1], prev->vec[2][1]);
}
/* This interpolation type induces movement even between identical columns. */
else {
hold = !ELEM(prev->ipo, BEZT_IPO_ELASTIC);
}
if (hold) {
info->flag |= ACTKEYBLOCK_FLAG_STATIC_HOLD | ACTKEYBLOCK_FLAG_ANY_HOLD;
}
}
/* Remember non-bezier interpolation info. */
if (prev->ipo != BEZT_IPO_BEZ) {
info->flag |= ACTKEYBLOCK_FLAG_NON_BEZIER;
}
info->sel = BEZT_ISSEL_ANY(prev) || BEZT_ISSEL_ANY(beztn);
}
static void add_keyblock_info(ActKeyColumn *col, const ActKeyBlockInfo *block)
{
/* New curve and block. */
if (col->totcurve <= 1 && col->totblock == 0) {
memcpy(&col->block, block, sizeof(ActKeyBlockInfo));
}
/* Existing curve. */
else {
col->block.conflict |= (col->block.flag ^ block->flag);
col->block.flag |= block->flag;
col->block.sel |= block->sel;
}
if (block->flag) {
col->totblock++;
}
}
static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len)
{
ActKeyColumn *col = static_cast<ActKeyColumn *>(keylist->key_columns.first);
if (bezt && bezt_len >= 2) {
ActKeyBlockInfo block;
/* Find the first key column while inserting dummy blocks. */
for (; col != nullptr && is_cfra_lt(col->cfra, bezt[0].vec[1][0]); col = col->next) {
add_keyblock_info(col, &dummy_keyblock);
}
BLI_assert(col != nullptr);
/* Insert real blocks. */
for (int v = 1; col != nullptr && v < bezt_len; v++, bezt++) {
/* Wrong order of bezier keys: resync position. */
if (is_cfra_lt(bezt[1].vec[1][0], bezt[0].vec[1][0])) {
/* Backtrack to find the right location. */
if (is_cfra_lt(bezt[1].vec[1][0], col->cfra)) {
ActKeyColumn *newcol = ED_keylist_find_exact_or_neighbor_column(keylist, col->cfra);
BLI_assert(newcol);
BLI_assert(newcol->cfra == col->cfra);
col = newcol;
/* The previous keyblock is garbage too. */
if (col->prev != nullptr) {
add_keyblock_info(col->prev, &dummy_keyblock);
}
}
continue;
}
/* In normal situations all keyframes are sorted. However, while keys are transformed, they
* may change order and then this assertion no longer holds. The effect is that the drawing
* isn't perfect during the transform; the "constant value" bars aren't updated until the
* transformation is confirmed. */
// BLI_assert(is_cfra_eq(col->cfra, bezt[0].vec[1][0]));
compute_keyblock_data(&block, bezt, bezt + 1);
for (; col != nullptr && is_cfra_lt(col->cfra, bezt[1].vec[1][0]); col = col->next) {
add_keyblock_info(col, &block);
}
BLI_assert(col != nullptr);
}
}
/* Insert dummy blocks at the end. */
for (; col != nullptr; col = col->next) {
add_keyblock_info(col, &dummy_keyblock);
}
}
/* Walk through columns and propagate blocks and totcurve.
*
* This must be called even by animation sources that don't generate
* keyblocks to keep the data structure consistent after adding columns.
*/
static void update_keyblocks(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len)
{
/* Find the curve count */
int max_curve = 0;
LISTBASE_FOREACH (ActKeyColumn *, col, &keylist->key_columns) {
max_curve = MAX2(max_curve, col->totcurve);
}
/* Propagate blocks to inserted keys */
ActKeyColumn *prev_ready = nullptr;
LISTBASE_FOREACH (ActKeyColumn *, col, &keylist->key_columns) {
/* Pre-existing column. */
if (col->totcurve > 0) {
prev_ready = col;
}
/* Newly inserted column, so copy block data from previous. */
else if (prev_ready != nullptr) {
col->totblock = prev_ready->totblock;
memcpy(&col->block, &prev_ready->block, sizeof(ActKeyBlockInfo));
}
col->totcurve = max_curve + 1;
}
/* Add blocks on top */
add_bezt_to_keyblocks_list(keylist, bezt, bezt_len);
}
/* --------- */
bool actkeyblock_is_valid(const ActKeyColumn *ac)
{
return ac != nullptr && ac->next != nullptr && ac->totblock > 0;
}
int actkeyblock_get_valid_hold(const ActKeyColumn *ac)
{
/* check that block is valid */
if (!actkeyblock_is_valid(ac)) {
return 0;
}
const int hold_mask = (ACTKEYBLOCK_FLAG_ANY_HOLD | ACTKEYBLOCK_FLAG_STATIC_HOLD);
return (ac->block.flag & ~ac->block.conflict) & hold_mask;
}
/* *************************** Keyframe List Conversions *************************** */
void summary_to_keylist(bAnimContext *ac, AnimKeylist *keylist, const int saction_flag)
{
if (ac) {
ListBase anim_data = {nullptr, nullptr};
/* get F-Curves to take keyframes from */
const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE;
ANIM_animdata_filter(
ac, &anim_data, filter, ac->data, static_cast<eAnimCont_Types>(ac->datatype));
/* loop through each F-Curve, grabbing the keyframes */
LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) {
/* Why not use all #eAnim_KeyType here?
* All of the other key types are actually "summaries" themselves,
* and will just end up duplicating stuff that comes up through
* standard filtering of just F-Curves. Given the way that these work,
* there isn't really any benefit at all from including them. - Aligorith */
switch (ale->datatype) {
case ALE_FCURVE:
fcurve_to_keylist(ale->adt, static_cast<FCurve *>(ale->data), keylist, saction_flag);
break;
case ALE_MASKLAY:
mask_to_keylist(ac->ads, static_cast<MaskLayer *>(ale->data), keylist);
break;
case ALE_GPFRAME:
gpl_to_keylist(ac->ads, static_cast<bGPDlayer *>(ale->data), keylist);
break;
default:
// printf("%s: datatype %d unhandled\n", __func__, ale->datatype);
break;
}
}
ANIM_animdata_freelist(&anim_data);
}
}
void scene_to_keylist(bDopeSheet *ads, Scene *sce, AnimKeylist *keylist, const int saction_flag)
{
bAnimContext ac = {nullptr};
ListBase anim_data = {nullptr, nullptr};
bAnimListElem dummy_chan = {nullptr};
if (sce == nullptr) {
return;
}
/* create a dummy wrapper data to work with */
dummy_chan.type = ANIMTYPE_SCENE;
dummy_chan.data = sce;
dummy_chan.id = &sce->id;
dummy_chan.adt = sce->adt;
ac.ads = ads;
ac.data = &dummy_chan;
ac.datatype = ANIMCONT_CHANNEL;
/* get F-Curves to take keyframes from */
const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FCURVESONLY;
ANIM_animdata_filter(
&ac, &anim_data, filter, ac.data, static_cast<eAnimCont_Types>(ac.datatype));
/* loop through each F-Curve, grabbing the keyframes */
LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) {
fcurve_to_keylist(ale->adt, static_cast<FCurve *>(ale->data), keylist, saction_flag);
}
ANIM_animdata_freelist(&anim_data);
}
void ob_to_keylist(bDopeSheet *ads, Object *ob, AnimKeylist *keylist, const int saction_flag)
{
bAnimContext ac = {nullptr};
ListBase anim_data = {nullptr, nullptr};
bAnimListElem dummy_chan = {nullptr};
Base dummy_base = {nullptr};
if (ob == nullptr) {
return;
}
/* create a dummy wrapper data to work with */
dummy_base.object = ob;
dummy_chan.type = ANIMTYPE_OBJECT;
dummy_chan.data = &dummy_base;
dummy_chan.id = &ob->id;
dummy_chan.adt = ob->adt;
ac.ads = ads;
ac.data = &dummy_chan;
ac.datatype = ANIMCONT_CHANNEL;
/* get F-Curves to take keyframes from */
const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FCURVESONLY;
ANIM_animdata_filter(
&ac, &anim_data, filter, ac.data, static_cast<eAnimCont_Types>(ac.datatype));
/* loop through each F-Curve, grabbing the keyframes */
LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) {
fcurve_to_keylist(ale->adt, static_cast<FCurve *>(ale->data), keylist, saction_flag);
}
ANIM_animdata_freelist(&anim_data);
}
void cachefile_to_keylist(bDopeSheet *ads,
CacheFile *cache_file,
AnimKeylist *keylist,
const int saction_flag)
{
if (cache_file == nullptr) {
return;
}
/* create a dummy wrapper data to work with */
bAnimListElem dummy_chan = {nullptr};
dummy_chan.type = ANIMTYPE_DSCACHEFILE;
dummy_chan.data = cache_file;
dummy_chan.id = &cache_file->id;
dummy_chan.adt = cache_file->adt;
bAnimContext ac = {nullptr};
ac.ads = ads;
ac.data = &dummy_chan;
ac.datatype = ANIMCONT_CHANNEL;
/* get F-Curves to take keyframes from */
ListBase anim_data = {nullptr, nullptr};
const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FCURVESONLY;
ANIM_animdata_filter(
&ac, &anim_data, filter, ac.data, static_cast<eAnimCont_Types>(ac.datatype));
/* loop through each F-Curve, grabbing the keyframes */
LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) {
fcurve_to_keylist(ale->adt, static_cast<FCurve *>(ale->data), keylist, saction_flag);
}
ANIM_animdata_freelist(&anim_data);
}
void fcurve_to_keylist(AnimData *adt, FCurve *fcu, AnimKeylist *keylist, const int saction_flag)
{
if (fcu && fcu->totvert && fcu->bezt) {
ED_keylist_reset_last_accessed(keylist);
/* apply NLA-mapping (if applicable) */
if (adt) {
ANIM_nla_mapping_apply_fcurve(adt, fcu, false, false);
}
/* Check if the curve is cyclic. */
bool is_cyclic = BKE_fcurve_is_cyclic(fcu) && (fcu->totvert >= 2);
bool do_extremes = (saction_flag & SACTION_SHOW_EXTREMES) != 0;
/* loop through beztriples, making ActKeysColumns */
BezTripleChain chain = {nullptr};
for (int v = 0; v < fcu->totvert; v++) {
chain.cur = &fcu->bezt[v];
/* Neighbor columns, accounting for being cyclic. */
if (do_extremes) {
chain.prev = (v > 0) ? &fcu->bezt[v - 1] :
is_cyclic ? &fcu->bezt[fcu->totvert - 2] :
nullptr;
chain.next = (v + 1 < fcu->totvert) ? &fcu->bezt[v + 1] :
is_cyclic ? &fcu->bezt[1] :
nullptr;
}
add_bezt_to_keycolumns_list(keylist, &chain);
}
/* Update keyblocks. */
update_keyblocks(keylist, fcu->bezt, fcu->totvert);
/* unapply NLA-mapping if applicable */
if (adt) {
ANIM_nla_mapping_apply_fcurve(adt, fcu, true, false);
}
}
}
void agroup_to_keylist(AnimData *adt,
bActionGroup *agrp,
AnimKeylist *keylist,
const int saction_flag)
{
if (agrp) {
/* loop through F-Curves */
LISTBASE_FOREACH (FCurve *, fcu, &agrp->channels) {
if (fcu->grp != agrp) {
break;
}
fcurve_to_keylist(adt, fcu, keylist, saction_flag);
}
}
}
void action_to_keylist(AnimData *adt, bAction *act, AnimKeylist *keylist, const int saction_flag)
{
if (act) {
/* loop through F-Curves */
LISTBASE_FOREACH (FCurve *, fcu, &act->curves) {
fcurve_to_keylist(adt, fcu, keylist, saction_flag);
}
}
}
void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, AnimKeylist *keylist, const bool active)
{
if (gpd && keylist) {
/* for now, just aggregate out all the frames, but only for visible layers */
LISTBASE_FOREACH_BACKWARD (bGPDlayer *, gpl, &gpd->layers) {
if ((gpl->flag & GP_LAYER_HIDE) == 0) {
if ((!active) || ((active) && (gpl->flag & GP_LAYER_SELECT))) {
gpl_to_keylist(ads, gpl, keylist);
}
}
}
}
}
void gpl_to_keylist(bDopeSheet * /*ads*/, bGPDlayer *gpl, AnimKeylist *keylist)
{
if (gpl && keylist) {
ED_keylist_reset_last_accessed(keylist);
/* Although the frames should already be in an ordered list,
* they are not suitable for displaying yet. */
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
add_gpframe_to_keycolumns_list(keylist, gpf);
}
update_keyblocks(keylist, nullptr, 0);
}
}
void mask_to_keylist(bDopeSheet * /*ads*/, MaskLayer *masklay, AnimKeylist *keylist)
{
if (masklay && keylist) {
ED_keylist_reset_last_accessed(keylist);
LISTBASE_FOREACH (MaskLayerShape *, masklay_shape, &masklay->splines_shapes) {
add_masklay_to_keycolumns_list(keylist, masklay_shape);
}
update_keyblocks(keylist, nullptr, 0);
}
}
}