UI: remove unnecessarily dead zones for pie menus

Previously when there were 5 or more menu items in a pie menu
the acceptable angle to select an item was limited to 45 degrees.

This makes sense when all 8 menu items are set, however it unnecessarily
restricts the range for menu items that don't have adjacent items.

Resolve using a 90 degree angle range then checking of the adjacent
buttons exist and are a better match.

This also resolves a very small dead-zone between adjacent buttons
for both 4 or 8 button pie menus. It was possible for a direction to
select neither. Compare the direction enum as a tie breaker.

Ref !112311.
This commit is contained in:
Campbell Barton
2023-09-13 12:25:04 +10:00
parent c7cd1c8d0b
commit a8db828618
2 changed files with 58 additions and 7 deletions

View File

@@ -98,6 +98,12 @@ enum RadialDirection {
UI_RADIAL_NW = 7,
};
/** Next direction (clockwise). */
#define UI_RADIAL_DIRECTION_NEXT(dir) RadialDirection((int(dir) + 1) % (int(UI_RADIAL_NW) + 1))
/** Previous direction (counter-clockwise). */
#define UI_RADIAL_DIRECTION_PREV(dir) \
RadialDirection(((int(dir) + int(UI_RADIAL_NW))) % (int(UI_RADIAL_NW) + 1))
extern const char ui_radial_dir_order[8];
extern const char ui_radial_dir_to_numpad[8];
extern const short ui_radial_dir_to_angle[8];

View File

@@ -180,21 +180,66 @@ void ui_but_pie_dir(RadialDirection dir, float vec[2])
static bool ui_but_isect_pie_seg(const uiBlock *block, const uiBut *but)
{
const float angle_range = (block->pie_data.flags & UI_PIE_DEGREES_RANGE_LARGE) ? M_PI_4 :
M_PI_4 / 2.0;
float vec[2];
if (block->pie_data.flags & UI_PIE_INVALID_DIR) {
return false;
}
ui_but_pie_dir(but->pie_dir, vec);
/* Plus/minus 45 degrees: `cosf(DEG2RADF(45)) == M_SQRT1_2`. */
const float angle_4th_cos = M_SQRT1_2;
/* Plus/minus 22.5 degrees: `cosf(DEG2RADF(22.5))`. */
const float angle_8th_cos = 0.9238795f;
if (saacos(dot_v2v2(vec, block->pie_data.pie_dir)) < angle_range) {
/* Use a large bias so edge-cases fall back to comparing with the adjacent direction. */
const float eps_bias = 1e-4;
float but_dir[2];
ui_but_pie_dir(but->pie_dir, but_dir);
const float angle_but_cos = dot_v2v2(but_dir, block->pie_data.pie_dir);
/* Outside range (with bias). */
if (angle_but_cos < angle_4th_cos - eps_bias) {
return false;
}
/* Inside range (with bias). */
if (angle_but_cos > angle_8th_cos + eps_bias) {
return true;
}
return false;
/* Check if adjacent direction is closer (with tie breaker). */
RadialDirection dir_adjacent_8th, dir_adjacent_4th;
if (angle_signed_v2v2(but_dir, block->pie_data.pie_dir) < 0.0f) {
dir_adjacent_8th = UI_RADIAL_DIRECTION_PREV(but->pie_dir);
dir_adjacent_4th = UI_RADIAL_DIRECTION_PREV(dir_adjacent_8th);
}
else {
dir_adjacent_8th = UI_RADIAL_DIRECTION_NEXT(but->pie_dir);
dir_adjacent_4th = UI_RADIAL_DIRECTION_NEXT(dir_adjacent_8th);
}
bool has_8th_adjacent = false;
if ((block->pie_data.flags & UI_PIE_DEGREES_RANGE_LARGE) == 0) {
LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) {
if (but->pie_dir == dir_adjacent_8th) {
if (!ELEM(but->type, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE)) {
has_8th_adjacent = true;
}
break;
}
}
}
/* Compare with the adjacent direction (even if there is no button). */
const RadialDirection dir_adjacent = has_8th_adjacent ? dir_adjacent_8th : dir_adjacent_4th;
float but_dir_adjacent[2];
ui_but_pie_dir(dir_adjacent, but_dir_adjacent);
const float angle_adjacent_cos = dot_v2v2(but_dir_adjacent, block->pie_data.pie_dir);
/* Tie breaker, so one of the buttons is always selected. */
if (UNLIKELY(angle_but_cos == angle_adjacent_cos)) {
return but->pie_dir > dir_adjacent;
}
return angle_but_cos > angle_adjacent_cos;
}
bool ui_but_contains_pt(const uiBut *but, float mx, float my)