UV: Box and lasso selection for partially intersecting edges

In UV edge mode, box and lasso selections allow edge selections only
when the entire edge is contained within the selection area. This
doesn't consider any edges that partially overlap with the selection
area.

This is  now fixed by adding a second pass, similar to how these
operators work for edit-mesh selections. Now if both operators are
unable to find any edges contained within the selection area, then
they will perform a second pass which checks for edges that partially
intersect with the selection area.

Now edge selection in the UV editor matches edit-mesh edge-selection
when drawing wire-frame.

Resolves T99443.

Ref D15362
This commit is contained in:
Siddhartha Jejurkar 2022-07-12 19:39:57 +10:00 committed by Campbell Barton
parent bb3a538843
commit 52b7f2b089
Notes: blender-bot 2023-02-14 11:25:11 +01:00
Referenced by issue #99443, Box selection in uv editor does not work as expected with edge mode
3 changed files with 110 additions and 0 deletions

View File

@ -309,6 +309,12 @@ float UI_view2d_view_to_region_y(const struct View2D *v2d, float y);
bool UI_view2d_view_to_region_clip(
const struct View2D *v2d, float x, float y, int *r_region_x, int *r_region_y) ATTR_NONNULL();
bool UI_view2d_view_to_region_segment_clip(const View2D *v2d,
const float xy_a[2],
const float xy_b[2],
int r_region_a[2],
int r_region_b[2]) ATTR_NONNULL();
/**
* Convert from 2d-view space to screen/region space
*

View File

@ -1695,6 +1695,41 @@ void UI_view2d_view_to_region_fl(
*r_region_y = v2d->mask.ymin + (y * BLI_rcti_size_y(&v2d->mask));
}
bool UI_view2d_view_to_region_segment_clip(const View2D *v2d,
const float xy_a[2],
const float xy_b[2],
int r_region_a[2],
int r_region_b[2])
{
rctf rect_unit;
rect_unit.xmin = rect_unit.ymin = 0.0f;
rect_unit.xmax = rect_unit.ymax = 1.0f;
/* Express given coordinates as proportional values. */
const float s_a[2] = {
(xy_a[0] - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur),
(xy_a[1] - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur),
};
const float s_b[2] = {
(xy_b[0] - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur),
(xy_b[1] - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur),
};
/* Set initial value in case coordinates lie outside bounds. */
r_region_a[0] = r_region_b[0] = r_region_a[1] = r_region_b[1] = V2D_IS_CLIPPED;
if (BLI_rctf_isect_segment(&rect_unit, s_a, s_b)) {
r_region_a[0] = (int)(v2d->mask.xmin + (s_a[0] * BLI_rcti_size_x(&v2d->mask)));
r_region_a[1] = (int)(v2d->mask.ymin + (s_a[1] * BLI_rcti_size_y(&v2d->mask)));
r_region_b[0] = (int)(v2d->mask.xmin + (s_b[0] * BLI_rcti_size_x(&v2d->mask)));
r_region_b[1] = (int)(v2d->mask.ymin + (s_b[1] * BLI_rcti_size_y(&v2d->mask)));
return true;
}
return false;
}
void UI_view2d_view_to_region_rcti(const View2D *v2d, const rctf *rect_src, rcti *rect_dst)
{
const float cur_size[2] = {BLI_rctf_size_x(&v2d->cur), BLI_rctf_size_y(&v2d->cur)};

View File

@ -3582,6 +3582,7 @@ static int uv_box_select_exec(bContext *C, wmOperator *op)
}
}
else if (use_edge && !pinned) {
bool do_second_pass = true;
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (!uvedit_face_visible_test(scene, efa)) {
continue;
@ -3596,11 +3597,35 @@ static int uv_box_select_exec(bContext *C, wmOperator *op)
uvedit_edge_select_set_with_sticky(
scene, em, l_prev, select, false, cd_loop_uv_offset);
changed = true;
do_second_pass = false;
}
l_prev = l;
luv_prev = luv;
}
}
/* Do a second pass if no complete edges could be selected.
* This matches wire-frame edit-mesh selection in the 3D view. */
if (do_second_pass) {
/* Second pass to check if edges partially overlap with the selection area (box). */
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (!uvedit_face_visible_test(scene, efa)) {
continue;
}
BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset);
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
if (BLI_rctf_isect_segment(&rectf, luv_prev->uv, luv->uv)) {
uvedit_edge_select_set_with_sticky(
scene, em, l_prev, select, false, cd_loop_uv_offset);
changed = true;
}
l_prev = l;
luv_prev = luv;
}
}
}
}
else {
/* other selection modes */
@ -3920,6 +3945,24 @@ static bool do_lasso_select_mesh_uv_is_point_inside(const ARegion *region,
return false;
}
static bool do_lasso_select_mesh_uv_is_edge_inside(const ARegion *region,
const rcti *clip_rect,
const int mcoords[][2],
const int mcoords_len,
const float co_test_a[2],
const float co_test_b[2])
{
int co_screen_a[2], co_screen_b[2];
if (UI_view2d_view_to_region_segment_clip(
&region->v2d, co_test_a, co_test_b, co_screen_a, co_screen_b) &&
BLI_rcti_isect_segment(clip_rect, co_screen_a, co_screen_b) &&
BLI_lasso_is_edge_inside(
mcoords, mcoords_len, UNPACK2(co_screen_a), UNPACK2(co_screen_b), V2D_IS_CLIPPED)) {
return true;
}
return false;
}
static bool do_lasso_select_mesh_uv(bContext *C,
const int mcoords[][2],
const int mcoords_len,
@ -3988,6 +4031,7 @@ static bool do_lasso_select_mesh_uv(bContext *C,
}
}
else if (use_edge) {
bool do_second_pass = true;
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (!uvedit_face_visible_test(scene, efa)) {
continue;
@ -4004,12 +4048,37 @@ static bool do_lasso_select_mesh_uv(bContext *C,
region, &rect, mcoords, mcoords_len, luv_prev->uv)) {
uvedit_edge_select_set_with_sticky(
scene, em, l_prev, select, false, cd_loop_uv_offset);
do_second_pass = false;
changed = true;
}
l_prev = l;
luv_prev = luv;
}
}
/* Do a second pass if no complete edges could be selected.
* This matches wire-frame edit-mesh selection in the 3D view. */
if (do_second_pass) {
/* Second pass to check if edges partially overlap with the selection area (lasso). */
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (!uvedit_face_visible_test(scene, efa)) {
continue;
}
BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset);
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
if (do_lasso_select_mesh_uv_is_edge_inside(
region, &rect, mcoords, mcoords_len, luv->uv, luv_prev->uv)) {
uvedit_edge_select_set_with_sticky(
scene, em, l_prev, select, false, cd_loop_uv_offset);
changed = true;
}
l_prev = l;
luv_prev = luv;
}
}
}
}
else { /* Vert Selection. */
BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false);