Animation: Armature symmetrize ebones refactor #106487

Manually merged
Sybren A. Stüvel merged 5 commits from cgtinker/blender:symmetrize-refactoring into main 2023-05-15 11:09:36 +02:00
1 changed files with 63 additions and 73 deletions
Showing only changes of commit 1172386176 - Show all commits

View File

@ -1096,71 +1096,6 @@ static EditBone *get_symmetrized_bone(bArmature *arm, EditBone *bone)
return (mirror != NULL) ? mirror : bone;
}
static void ED_armature_set_symmetrizable_ebone_selection(bArmature *arm, int axis, int direction)
{
/* Deselects ebones depending on input axis and direction.
* A symmetrizable selection contains selected ebones of the input direction
* and unique selected bones with an unique flippable name.*/
EditBone *ebone_iter;
for (ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) {
if (!(EBONE_VISIBLE(arm, ebone_iter) && (ebone_iter->flag & BONE_SELECTED))) {
/* ignore selected bones which are invisble */
continue;
}
char name_flip[MAXBONENAME];
if (ebone_iter == NULL) {
continue;
}
BLI_string_flip_side_name(name_flip, ebone_iter->name, false, sizeof(name_flip));
if (STREQ(name_flip, ebone_iter->name)) {
/* if the name matches, we don't have the potential to be mirrored, just skip */
ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
continue;
}
EditBone *ebone = ED_armature_ebone_find_name(arm->edbo, name_flip);
if (!ebone) {
continue;
}
if ((ebone->flag & BONE_SELECTED) == 0) {
/* ebone is only once selected per side */
continue;
}
/* complicated - choose which direction to copy */
float axis_delta;
axis_delta = ebone->head[axis] - ebone_iter->head[axis];
if (axis_delta == 0.0f) {
axis_delta = ebone->tail[axis] - ebone_iter->tail[axis];
if (axis_delta == 0.0f) {
/* Both mirrored bones exist and point to each other and overlap exactly.
* in this case there's no well defined solution, so de-select both and skip.
*/
ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
continue;
}
}
// deselect depending on direction
if (((axis_delta < 0.0f) ? -1 : 1) == direction) {
ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
}
else {
ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
}
}
}
/**

No need to have an ED_armature prefix, those are only for functions that are declared in the header files (and thus part of Blender's internal API).

Also axis and direction can be declared as const int.

No need to have an `ED_armature` prefix, those are only for functions that are declared in the header files (and thus part of Blender's internal API). Also `axis` and `direction` can be declared as `const int`.
* near duplicate of #armature_duplicate_selected_exec,
* except for parenting part (keep in sync)
@ -1192,16 +1127,71 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op)
preEditBoneDuplicate(arm->edbo);
/* Select mirrored bones */
ED_armature_set_symmetrizable_ebone_selection(arm, axis, direction);
/* Deselect ebones depending on input axis and direction.
* A symmetrizable selection contains selected ebones of the input direction
* and unique selected bones with an unique flippable name.
*
* Storing temp ptrs to mirrored unselected ebones. */
for (ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) {
if (EBONE_VISIBLE(arm, ebone_iter) && (ebone_iter->flag & BONE_SELECTED)) {
EditBone *ebone;
/* Set temp pointer to mirrored ebones */
ebone = ED_armature_ebone_get_mirrored(arm->edbo, ebone_iter);
if (ebone) {
ebone_iter->temp.ebone = ebone;
if (!(EBONE_VISIBLE(arm, ebone_iter) && (ebone_iter->flag & BONE_SELECTED))) {
/* Skipping invisible selected bones. */
continue;
}
char name_flip[MAXBONENAME];
if (ebone_iter == NULL) {
continue;
}
BLI_string_flip_side_name(name_flip, ebone_iter->name, false, sizeof(name_flip));
if (STREQ(name_flip, ebone_iter->name)) {
/* Skipping ebones without flippable as they don't have the potential to be mirrored. */
ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
continue;
}
EditBone *ebone = ED_armature_ebone_find_name(arm->edbo, name_flip);
if (!ebone) {
/* The ebone_iter is unique and mirrorable. */
continue;
}
if (ebone->flag & BONE_SELECTED) {
/* The mirrored ebone and the ebone_iter are selected.
* Deselect based on the input direction and axis. */
float axis_delta;
axis_delta = ebone->head[axis] - ebone_iter->head[axis];
if (axis_delta == 0.0f) {
/* The ebone heads are overlapping. */
axis_delta = ebone->tail[axis] - ebone_iter->tail[axis];
if (axis_delta == 0.0f) {
/* Both mirrored bones point to each other and overlap exactly.
* In this case there's no well defined solution, so de-select both and skip. */
ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
continue;
}
}
/* Deselect depending on direction. */
if (((axis_delta < 0.0f) ? -1 : 1) == direction) {
/* Don't store temp ptr if the iter_bone gets deselected.
* In this case, the ebone.temp should point to the ebone_iter. */
ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
continue;
}
ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
}
/* Set temp pointer to mirrored ebones */
ebone = ED_armature_ebone_get_mirrored(arm->edbo, ebone_iter);

When you're here in the code, ebone is guaranteed to be non-NULL.
And even if it were to be NULL, setting ebone_iter->temp.ebone = NULL in that case also wouldn't hurt.

When you're here in the code, `ebone` is guaranteed to be non-`NULL`. And even if it were to be `NULL`, setting `ebone_iter->temp.ebone = NULL` in that case also wouldn't hurt.

Right, I've overlooked that, thanks.

Right, I've overlooked that, thanks.
if (ebone) {
ebone_iter->temp.ebone = ebone;
}
}