diff options
-rw-r--r-- | source/blender/editors/include/UI_view2d.h | 6 | ||||
-rw-r--r-- | source/blender/editors/interface/view2d.cc | 35 | ||||
-rw-r--r-- | source/blender/editors/uvedit/uvedit_select.c | 69 |
3 files changed, 110 insertions, 0 deletions
diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index 5c4eb254462..e508c96b4f1 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -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 * diff --git a/source/blender/editors/interface/view2d.cc b/source/blender/editors/interface/view2d.cc index ee4bfd351ae..1bf7e25b154 100644 --- a/source/blender/editors/interface/view2d.cc +++ b/source/blender/editors/interface/view2d.cc @@ -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)}; diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index db834f6a0fd..b5a564fd984 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -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( + ®ion->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); |