diff options
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/blenkernel/BKE_key.h | 4 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/key.c | 88 | ||||
-rw-r--r-- | source/blender/editors/object/object_shapekey.c | 122 |
3 files changed, 136 insertions, 78 deletions
diff --git a/source/blender/blenkernel/BKE_key.h b/source/blender/blenkernel/BKE_key.h index 6183df8666d..892c42b4588 100644 --- a/source/blender/blenkernel/BKE_key.h +++ b/source/blender/blenkernel/BKE_key.h @@ -97,6 +97,10 @@ float (*BKE_key_convert_to_vertcos(struct Object *ob, struct KeyBlock *kb))[3]; void BKE_key_convert_from_vertcos(struct Object *ob, struct KeyBlock *kb, float (*vertCos)[3]); void BKE_key_convert_from_offset(struct Object *ob, struct KeyBlock *kb, float (*ofs)[3]); +/* other management */ +bool BKE_keyblock_move(struct Object *ob, int org_index, int new_index); + + /* key.c */ extern int slurph_opt; 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; +} diff --git a/source/blender/editors/object/object_shapekey.c b/source/blender/editors/object/object_shapekey.c index 85104c14395..4a24ab66721 100644 --- a/source/blender/editors/object/object_shapekey.c +++ b/source/blender/editors/object/object_shapekey.c @@ -301,6 +301,16 @@ static int shape_key_mode_exists_poll(bContext *C) (BKE_keyblock_from_object(ob) != NULL); } +static int shape_key_move_poll(bContext *C) +{ + /* Same as shape_key_mode_exists_poll above, but ensure we have at least two shapes! */ + Object *ob = ED_object_context(C); + ID *data = (ob) ? ob->data : NULL; + Key *key = BKE_key_from_object(ob); + + return (ob && !ob->id.lib && data && !data->lib && ob->mode != OB_MODE_EDIT && key && key->totkey > 1); +} + static int shape_key_poll(bContext *C) { Object *ob = ED_object_context(C); @@ -482,86 +492,40 @@ void OBJECT_OT_shape_key_mirror(wmOperatorType *ot) } +enum { + KB_MOVE_TOP = -2, + KB_MOVE_UP = -1, + KB_MOVE_DOWN = 1, + KB_MOVE_BOTTOM = 2, +}; + static int shape_key_move_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); - Key *key = BKE_key_from_object(ob); - if (!key) { - return OPERATOR_CANCELLED; + Key *key = BKE_key_from_object(ob); + const int type = RNA_enum_get(op->ptr, "type"); + const int totkey = key->totkey; + const int act_index = ob->shapenr - 1; + int new_index; + + switch (type) { + case KB_MOVE_TOP: + /* Replace the ref key only if we're at the top already (only for relative keys) */ + new_index = (ELEM(act_index, 0, 1) || key->type == KEY_NORMAL) ? 0 : 1; + break; + case KB_MOVE_BOTTOM: + new_index = totkey - 1; + break; + case KB_MOVE_UP: + case KB_MOVE_DOWN: + default: + new_index = (totkey + act_index + type) % totkey; + break; } - { - KeyBlock *kb, *kb_other, *kb_iter; - const int type = RNA_enum_get(op->ptr, "type"); - const int shape_tot = key->totkey; - const int shapenr_act = ob->shapenr - 1; - const int shapenr_swap = (shape_tot + shapenr_act + type) % shape_tot; - - kb = BLI_findlink(&key->block, shapenr_act); - if (!kb || shape_tot == 1) { - return OPERATOR_CANCELLED; - } - - if (type == -1) { - /* move back */ - kb_other = kb->prev; - BLI_remlink(&key->block, kb); - BLI_insertlinkbefore(&key->block, kb_other, kb); - } - else { - /* move next */ - kb_other = kb->next; - BLI_remlink(&key->block, kb); - BLI_insertlinkafter(&key->block, kb_other, kb); - } - - ob->shapenr = shapenr_swap + 1; - - /* for relative shape keys */ - if (kb_other) { - for (kb_iter = key->block.first; kb_iter; kb_iter = kb_iter->next) { - if (kb_iter->relative == shapenr_act) { - kb_iter->relative = shapenr_swap; - } - else if (kb_iter->relative == shapenr_swap) { - kb_iter->relative = shapenr_act; - } - } - } - /* First key became last, or vice-versa, we have to change all keys' relative value. */ - else { - for (kb_iter = key->block.first; kb_iter; kb_iter = kb_iter->next) { - if (kb_iter->relative == shapenr_act) { - kb_iter->relative = shapenr_swap; - } - else { - kb_iter->relative += type; - } - } - } - - /* for absolute shape keys */ - if (kb_other) { - SWAP(float, kb_other->pos, kb->pos); - } - /* First key became last, or vice-versa, we have to change all keys' pos value. */ - else { - float pos = kb->pos; - if (type == -1) { - for (kb_iter = key->block.first; kb_iter; kb_iter = kb_iter->next) { - SWAP(float, kb_iter->pos, pos); - } - } - else { - for (kb_iter = key->block.last; kb_iter; kb_iter = kb_iter->prev) { - SWAP(float, kb_iter->pos, pos); - } - } - } - - /* First key is refkey, matches interface and BKE_key_sort */ - key->refkey = key->block.first; + if (!BKE_keyblock_move(ob, act_index, new_index)) { + return OPERATOR_CANCELLED; } DAG_id_tag_update(&ob->id, OB_RECALC_DATA); @@ -573,9 +537,11 @@ static int shape_key_move_exec(bContext *C, wmOperator *op) void OBJECT_OT_shape_key_move(wmOperatorType *ot) { static EnumPropertyItem slot_move[] = { - {-1, "UP", 0, "Up", ""}, - {1, "DOWN", 0, "Down", ""}, - {0, NULL, 0, NULL, NULL} + {KB_MOVE_TOP, "TOP", 0, "Top", "Top of the list"}, + {KB_MOVE_UP, "UP", 0, "Up", ""}, + {KB_MOVE_DOWN, "DOWN", 0, "Down", ""}, + {KB_MOVE_BOTTOM, "BOTTOM", 0, "Bottom", "Bottom of the list"}, + { 0, NULL, 0, NULL, NULL } }; /* identifiers */ @@ -584,7 +550,7 @@ void OBJECT_OT_shape_key_move(wmOperatorType *ot) ot->description = "Move the active shape key up/down in the list"; /* api callbacks */ - ot->poll = shape_key_mode_poll; + ot->poll = shape_key_move_poll; ot->exec = shape_key_move_exec; /* flags */ |