diff options
Diffstat (limited to 'source/blender/blenkernel/intern/key.c')
-rw-r--r-- | source/blender/blenkernel/intern/key.c | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 2dc615c19f9..5cca948cd49 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -2055,3 +2055,91 @@ void BKE_key_convert_from_offset(Object *ob, KeyBlock *kb, float (*ofs)[3]) } } } + +/* ==========================================================*/ + +/** Move shape key from org_index to new_index. Safe, clamps index to valid range, updates reference keys, + * the object's active shape index, the 'frame' value in case of absolute keys, etc. + * Note indices are expected in real values (not 'fake' shapenr +1 ones). + * + * \param org_index if < 0, current object's active shape will be used as skey to move. + * \return true if something was done, else false. + */ +bool BKE_keyblock_move(Object *ob, int org_index, int new_index) +{ + Key *key = BKE_key_from_object(ob); + KeyBlock *kb; + const int act_index = ob->shapenr - 1; + const int totkey = key->totkey; + int i; + bool rev, in_range = false; + + if (org_index < 0) { + org_index = act_index; + } + + CLAMP(new_index, 0, key->totkey - 1); + CLAMP(org_index, 0, key->totkey - 1); + + if (new_index == org_index) { + return false; + } + + rev = ((new_index - org_index) < 0) ? true : false; + + /* We swap 'org' element with its previous/next neighbor (depending on direction of the move) repeatedly, + * until we reach final position. + * This allows us to only loop on the list once! */ + for (kb = (rev ? key->block.last : key->block.first), i = (rev ? totkey - 1 : 0); + kb; + kb = (rev ? kb->prev : kb->next), rev ? i-- : i++) + { + if (i == org_index) { + in_range = true; /* Start list items swapping... */ + } + else if (i == new_index) { + in_range = false; /* End list items swapping. */ + } + + if (in_range) { + KeyBlock *other_kb = rev ? kb->prev : kb->next; + + /* Swap with previous/next list item. */ + BLI_swaplinks(&key->block, kb, other_kb); + + /* Swap absolute positions. */ + SWAP(float, kb->pos, other_kb->pos); + + kb = other_kb; + } + + /* Adjust relative indices, this has to be done on the whole list! */ + if (kb->relative == org_index) { + kb->relative = new_index; + } + else if (kb->relative < org_index && kb->relative >= new_index) { + /* remove after, insert before this index */ + kb->relative++; + } + else if (kb->relative > org_index && kb->relative <= new_index) { + /* remove before, insert after this index */ + kb->relative--; + } + } + + /* Need to update active shape number if it's affected, same principle as for relative indices above. */ + if (org_index == act_index) { + ob->shapenr = new_index + 1; + } + else if (act_index < org_index && act_index >= new_index) { + ob->shapenr++; + } + else if (act_index > org_index && act_index <= new_index) { + ob->shapenr--; + } + + /* First key is always refkey, matches interface and BKE_key_sort */ + key->refkey = key->block.first; + + return true; +} |