diff options
Diffstat (limited to 'source/blender/editors/mesh/editmesh_select.c')
-rw-r--r-- | source/blender/editors/mesh/editmesh_select.c | 1185 |
1 files changed, 861 insertions, 324 deletions
diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index a0c4122af3a..4920a5af41b 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -36,6 +36,7 @@ #include "BLI_linklist.h" #include "BLI_linklist_stack.h" #include "BLI_math.h" +#include "BLI_math_bits.h" #include "BLI_rand.h" #include "BLI_array.h" @@ -52,6 +53,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_enum_types.h" #include "ED_mesh.h" #include "ED_screen.h" @@ -196,11 +198,11 @@ bool EDBM_backbuf_border_init(ViewContext *vc, short xmin, short ymin, short xma unsigned int *dr; int a; - if (vc->obedit == NULL || vc->v3d->drawtype < OB_SOLID || (vc->v3d->flag & V3D_ZBUF_SELECT) == 0) { + if (vc->obedit == NULL || !V3D_IS_ZBUF(vc->v3d)) { return false; } - buf = view3d_read_backbuf(vc, xmin, ymin, xmax, ymax); + buf = ED_view3d_backbuf_read(vc, xmin, ymin, xmax, ymax); if (buf == NULL) return false; if (bm_vertoffs == 0) return false; @@ -271,11 +273,11 @@ bool EDBM_backbuf_border_mask_init(ViewContext *vc, const int mcords[][2], short return false; } } - else if (vc->v3d->drawtype < OB_SOLID || (vc->v3d->flag & V3D_ZBUF_SELECT) == 0) { + else if (!V3D_IS_ZBUF(vc->v3d)) { return false; } - buf = view3d_read_backbuf(vc, xmin, ymin, xmax, ymax); + buf = ED_view3d_backbuf_read(vc, xmin, ymin, xmax, ymax); if (buf == NULL) return false; if (bm_vertoffs == 0) return false; @@ -320,13 +322,13 @@ bool EDBM_backbuf_circle_init(ViewContext *vc, short xs, short ys, short rads) return false; } } - else if (vc->v3d->drawtype < OB_SOLID || (vc->v3d->flag & V3D_ZBUF_SELECT) == 0) { + else if (!V3D_IS_ZBUF(vc->v3d)) { return false; } xmin = xs - rads; xmax = xs + rads; ymin = ys - rads; ymax = ys + rads; - buf = view3d_read_backbuf(vc, xmin, ymin, xmax, ymax); + buf = ED_view3d_backbuf_read(vc, xmin, ymin, xmax, ymax); if (bm_vertoffs == 0) return false; if (buf == NULL) return false; @@ -350,194 +352,369 @@ bool EDBM_backbuf_circle_init(ViewContext *vc, short xs, short ys, short rads) } + +/* -------------------------------------------------------------------- */ + +/** \name Find Nearest Vert/Edge/Face + * + * \note Screen-space manhatten distances are used here, + * since its faster and good enough for the purpose of selection. + * + * \note \a dist_bias is used so we can bias against selected items. + * when choosing between elements of a single type, but return the real distance + * to avoid the bias interfering with distance comparisons when mixing types. + * \{ */ + +#define FIND_NEAR_SELECT_BIAS 5 +#define FIND_NEAR_CYCLE_THRESHOLD_MIN 3 + +struct NearestVertUserData_Hit { + float dist; + float dist_bias; + int index; + BMVert *vert; +}; + +struct NearestVertUserData { + float mval_fl[2]; + bool use_select_bias; + bool use_cycle; + int cycle_index_prev; + + struct NearestVertUserData_Hit hit; + struct NearestVertUserData_Hit hit_cycle; +}; + static void findnearestvert__doClosest(void *userData, BMVert *eve, const float screen_co[2], int index) { - struct { float mval_fl[2], pass, select, strict; float dist, lastIndex, closestIndex; BMVert *closest; } *data = userData; + struct NearestVertUserData *data = userData; + float dist_test, dist_test_bias; - if (data->pass == 0) { - if (index <= data->lastIndex) - return; - } - else { - if (index > data->lastIndex) - return; + dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co); + + if (data->use_select_bias && BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + dist_test_bias += FIND_NEAR_SELECT_BIAS; } - if (data->dist > 3) { - float dist_test = len_manhattan_v2v2(data->mval_fl, screen_co); - if (BM_elem_flag_test(eve, BM_ELEM_SELECT) == data->select) { - if (data->strict == 1) { - return; - } - else { - dist_test += 5; - } - } + if (dist_test_bias < data->hit.dist_bias) { + data->hit.dist_bias = dist_test_bias; + data->hit.dist = dist_test; + data->hit.index = index; + data->hit.vert = eve; + } - if (dist_test < data->dist) { - data->dist = dist_test; - data->closest = eve; - data->closestIndex = index; + if (data->use_cycle) { + if ((data->hit_cycle.vert == NULL) && + (index > data->cycle_index_prev) && + (dist_test_bias < FIND_NEAR_CYCLE_THRESHOLD_MIN)) + { + data->hit_cycle.dist_bias = dist_test_bias; + data->hit_cycle.dist = dist_test; + data->hit_cycle.index = index; + data->hit_cycle.vert = eve; } } } - -static bool findnearestvert__backbufIndextest(void *handle, unsigned int index) -{ - BMEditMesh *em = (BMEditMesh *)handle; - BMVert *eve = BM_vert_at_index_find(em->bm, index - 1); - return !(eve && BM_elem_flag_test(eve, BM_ELEM_SELECT)); -} /** - * findnearestvert - * - * dist (in/out): minimal distance to the nearest and at the end, actual distance - * sel: selection bias - * if SELECT, selected vertice are given a 5 pixel bias to make them further than unselect verts - * if 0, unselected vertice are given the bias - * strict: if 1, the vertice corresponding to the sel parameter are ignored and not just biased + * Nearest vertex under the cursor. + * + * \param r_dist (in/out), minimal distance to the nearest and at the end, actual distance + * \param use_select_bias + * - When true, selected vertice are given a 5 pixel bias to make them further than unselect verts. + * - When false, unselected vertice are given the bias. + * \param use_cycle Cycle over elements within #FIND_NEAR_CYCLE_THRESHOLD_MIN in order of index. */ -BMVert *EDBM_vert_find_nearest(ViewContext *vc, float *r_dist, const bool sel, const bool strict) +BMVert *EDBM_vert_find_nearest_ex( + ViewContext *vc, float *r_dist, + const bool use_select_bias, bool use_cycle) { - if (vc->v3d->drawtype > OB_WIRE && (vc->v3d->flag & V3D_ZBUF_SELECT)) { - float distance; + BMesh *bm = vc->em->bm; + + if (V3D_IS_ZBUF(vc->v3d)) { + const int dist_px = ED_view3d_backbuf_sample_size_clamp(vc->ar, *r_dist); + float dist_test; unsigned int index; BMVert *eve; - if (strict) { - index = view3d_sample_backbuf_rect(vc, vc->mval, 50, bm_wireoffs, 0xFFFFFF, &distance, - strict, vc->em, findnearestvert__backbufIndextest); - } - else { - index = view3d_sample_backbuf_rect(vc, vc->mval, 50, bm_wireoffs, 0xFFFFFF, &distance, - 0, NULL, NULL); - } - - eve = index ? BM_vert_at_index_find(vc->em->bm, index - 1) : NULL; + index = ED_view3d_backbuf_sample_rect( + vc, vc->mval, dist_px, bm_wireoffs, 0xFFFFFF, &dist_test); + eve = index ? BM_vert_at_index_find_or_table(bm, index - 1) : NULL; - if (eve && distance < *r_dist) { - *r_dist = distance; - return eve; - } - else { - return NULL; + if (eve) { + if (dist_test < *r_dist) { + *r_dist = dist_test; + return eve; + } } - + return NULL; } else { - struct { float mval_fl[2], pass, select, strict; float dist, lastIndex, closestIndex; BMVert *closest; } data; - static int lastSelectedIndex = 0; - static BMVert *lastSelected = NULL; - - if (lastSelected && BM_vert_at_index_find(vc->em->bm, lastSelectedIndex) != lastSelected) { - lastSelectedIndex = 0; - lastSelected = NULL; + struct NearestVertUserData data = {{0}}; + const struct NearestVertUserData_Hit *hit; + const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_DEFAULT; + + static int prev_select_index = 0; + static const BMVert *prev_select_elem = NULL; + + if ((use_cycle == false) || + (prev_select_elem && (prev_select_elem != BM_vert_at_index_find_or_table(bm, prev_select_index)))) + { + prev_select_index = 0; + prev_select_elem = NULL; } - data.lastIndex = lastSelectedIndex; data.mval_fl[0] = vc->mval[0]; data.mval_fl[1] = vc->mval[1]; - data.select = sel ? BM_ELEM_SELECT : 0; - data.dist = *r_dist; - data.strict = strict; - data.closest = NULL; - data.closestIndex = 0; - - data.pass = 0; + data.use_select_bias = use_select_bias; + data.use_cycle = use_cycle; + data.hit.dist = data.hit_cycle.dist = \ + data.hit.dist_bias = data.hit_cycle.dist_bias = *r_dist; + data.cycle_index_prev = prev_select_index; ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); + mesh_foreachScreenVert(vc, findnearestvert__doClosest, &data, clip_flag); - mesh_foreachScreenVert(vc, findnearestvert__doClosest, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + hit = (data.use_cycle && data.hit_cycle.vert) ? &data.hit_cycle : &data.hit; + *r_dist = hit->dist; - if (data.dist > 3) { - data.pass = 1; - mesh_foreachScreenVert(vc, findnearestvert__doClosest, &data, V3D_PROJ_TEST_CLIP_DEFAULT); - } + prev_select_elem = hit->vert; + prev_select_index = hit->index; - *r_dist = data.dist; - lastSelected = data.closest; - lastSelectedIndex = data.closestIndex; + return hit->vert; + } +} - return data.closest; +BMVert *EDBM_vert_find_nearest(ViewContext *vc, float *r_dist) +{ + return EDBM_vert_find_nearest_ex(vc, r_dist, false, false); +} + +/* find the distance to the edge we already have */ +struct NearestEdgeUserData_ZBuf { + float mval_fl[2]; + float dist; + const BMEdge *edge_test; +}; + +static void find_nearest_edge_center__doZBuf( + void *userData, BMEdge *eed, + const float screen_co_a[2], const float screen_co_b[2], + int UNUSED(index)) +{ + struct NearestEdgeUserData_ZBuf *data = userData; + + if (eed == data->edge_test) { + float dist_test; + float screen_co_mid[2]; + + mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b); + dist_test = len_manhattan_v2v2(data->mval_fl, screen_co_mid); + + if (dist_test < data->dist) { + data->dist = dist_test; + } } } +struct NearestEdgeUserData_Hit { + float dist; + float dist_bias; + int index; + BMEdge *edge; + + /* edges only, un-biased manhatten distance to which ever edge we pick + * (not used for choosing) */ + float dist_center; +}; + +struct NearestEdgeUserData { + ViewContext vc; + float mval_fl[2]; + bool use_select_bias; + bool use_cycle; + int cycle_index_prev; + + struct NearestEdgeUserData_Hit hit; + struct NearestEdgeUserData_Hit hit_cycle; +}; + /* note; uses v3d, so needs active 3d window */ -static void findnearestedge__doClosest(void *userData, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int UNUSED(index)) +static void find_nearest_edge__doClosest( + void *userData, BMEdge *eed, + const float screen_co_a[2], const float screen_co_b[2], + int index) { - struct { ViewContext vc; float mval_fl[2]; float dist; BMEdge *closest; } *data = userData; - int distance; + struct NearestEdgeUserData *data = userData; + float dist_test, dist_test_bias; - distance = dist_to_line_segment_v2(data->mval_fl, screen_co_a, screen_co_b); - - if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) { - distance += 5; + float fac = line_point_factor_v2(data->mval_fl, screen_co_a, screen_co_b); + float screen_co[2]; + + if (fac <= 0.0f) { + fac = 0.0f; + copy_v2_v2(screen_co, screen_co_a); + } + else if (fac >= 1.0f) { + fac = 1.0f; + copy_v2_v2(screen_co, screen_co_b); + } + else { + interp_v2_v2v2(screen_co, screen_co_a, screen_co_b, fac); } - if (distance < data->dist) { - if (data->vc.rv3d->rflag & RV3D_CLIPPING) { - float lambda = line_point_factor_v2(data->mval_fl, screen_co_a, screen_co_b); - float vec[3]; + dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co); - vec[0] = eed->v1->co[0] + lambda * (eed->v2->co[0] - eed->v1->co[0]); - vec[1] = eed->v1->co[1] + lambda * (eed->v2->co[1] - eed->v1->co[1]); - vec[2] = eed->v1->co[2] + lambda * (eed->v2->co[2] - eed->v1->co[2]); + if (data->use_select_bias && BM_elem_flag_test(eed, BM_ELEM_SELECT)) { + dist_test += FIND_NEAR_SELECT_BIAS; + } - if (ED_view3d_clipping_test(data->vc.rv3d, vec, true) == 0) { - data->dist = distance; - data->closest = eed; - } + if (data->vc.rv3d->rflag & RV3D_CLIPPING) { + float vec[3]; + + interp_v3_v3v3(vec, eed->v1->co, eed->v2->co, fac); + if (ED_view3d_clipping_test(data->vc.rv3d, vec, true)) { + return; } - else { - data->dist = distance; - data->closest = eed; + } + + if (dist_test_bias < data->hit.dist_bias) { + float screen_co_mid[2]; + + data->hit.dist_bias = dist_test_bias; + data->hit.dist = dist_test; + data->hit.index = index; + data->hit.edge = eed; + + mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b); + data->hit.dist_center = len_manhattan_v2v2(data->mval_fl, screen_co_mid); + } + + if (data->use_cycle) { + if ((data->hit_cycle.edge == NULL) && + (index > data->cycle_index_prev) && + (dist_test_bias < FIND_NEAR_CYCLE_THRESHOLD_MIN)) + { + float screen_co_mid[2]; + + data->hit_cycle.dist_bias = dist_test_bias; + data->hit_cycle.dist = dist_test; + data->hit_cycle.index = index; + data->hit_cycle.edge = eed; + + mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b); + data->hit_cycle.dist_center = len_manhattan_v2v2(data->mval_fl, screen_co_mid); } } } -BMEdge *EDBM_edge_find_nearest(ViewContext *vc, float *r_dist) + +BMEdge *EDBM_edge_find_nearest_ex( + ViewContext *vc, float *r_dist, + float *r_dist_center, + const bool use_select_bias, const bool use_cycle, + BMEdge **r_eed_zbuf) { + BMesh *bm = vc->em->bm; - if (vc->v3d->drawtype > OB_WIRE && (vc->v3d->flag & V3D_ZBUF_SELECT)) { - float distance; + if (V3D_IS_ZBUF(vc->v3d)) { + const int dist_px = ED_view3d_backbuf_sample_size_clamp(vc->ar, *r_dist); + float dist_test = 0.0f; unsigned int index; BMEdge *eed; - view3d_validate_backbuf(vc); - - index = view3d_sample_backbuf_rect(vc, vc->mval, 50, bm_solidoffs, bm_wireoffs, &distance, 0, NULL, NULL); - eed = index ? BM_edge_at_index_find(vc->em->bm, index - 1) : NULL; - - if (eed && distance < *r_dist) { - *r_dist = distance; - return eed; + ED_view3d_backbuf_validate(vc); + + index = ED_view3d_backbuf_sample_rect(vc, vc->mval, dist_px, bm_solidoffs, bm_wireoffs, &dist_test); + eed = index ? BM_edge_at_index_find_or_table(bm, index - 1) : NULL; + + if (r_eed_zbuf) { + *r_eed_zbuf = eed; } - else { - return NULL; + + /* exception for faces (verts don't need this) */ + if (r_dist_center && eed) { + struct NearestEdgeUserData_ZBuf data; + + data.mval_fl[0] = vc->mval[0]; + data.mval_fl[1] = vc->mval[1]; + data.dist = FLT_MAX; + data.edge_test = eed; + + ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); + + mesh_foreachScreenEdge(vc, find_nearest_edge_center__doZBuf, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + + *r_dist_center = data.dist; + } + /* end exception */ + + if (eed) { + if (dist_test < *r_dist) { + *r_dist = dist_test; + return eed; + } } + return NULL; } else { - struct { ViewContext vc; float mval_fl[2]; float dist; BMEdge *closest; } data; + struct NearestEdgeUserData data = {{0}}; + const struct NearestEdgeUserData_Hit *hit; + /* interpolate along the edge before doing a clipping plane test */ + const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_DEFAULT & ~V3D_PROJ_TEST_CLIP_BB; + + static int prev_select_index = 0; + static const BMEdge *prev_select_elem = NULL; + + if ((use_cycle == false) || + (prev_select_elem && (prev_select_elem != BM_edge_at_index_find_or_table(bm, prev_select_index)))) + { + prev_select_index = 0; + prev_select_elem = NULL; + } data.vc = *vc; data.mval_fl[0] = vc->mval[0]; data.mval_fl[1] = vc->mval[1]; - data.dist = *r_dist; - data.closest = NULL; + data.use_select_bias = use_select_bias; + data.use_cycle = use_cycle; + data.hit.dist = data.hit_cycle.dist = \ + data.hit.dist_bias = data.hit_cycle.dist_bias = *r_dist; + data.cycle_index_prev = prev_select_index; + ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); + mesh_foreachScreenEdge(vc, find_nearest_edge__doClosest, &data, clip_flag); + + hit = (data.use_cycle && data.hit_cycle.edge) ? &data.hit_cycle : &data.hit; + *r_dist = hit->dist; + if (r_dist_center) { + *r_dist_center = hit->dist_center; + } - mesh_foreachScreenEdge(vc, findnearestedge__doClosest, &data, V3D_PROJ_TEST_CLIP_WIN); + prev_select_elem = hit->edge; + prev_select_index = hit->index; - *r_dist = data.dist; - return data.closest; + return hit->edge; } } -static void findnearestface__getDistance(void *userData, BMFace *efa, const float screen_co[2], int UNUSED(index)) +BMEdge *EDBM_edge_find_nearest( + ViewContext *vc, float *r_dist) { - struct { float mval_fl[2]; float dist; BMFace *toFace; } *data = userData; + return EDBM_edge_find_nearest_ex(vc, r_dist, false, false, false, NULL); +} + +/* find the distance to the face we already have */ +struct NearestFaceUserData_ZBuf { + float mval_fl[2]; + float dist; + const BMFace *face_test; +}; - if (efa == data->toFace) { +static void find_nearest_face_center__doZBuf(void *userData, BMFace *efa, const float screen_co[2], int UNUSED(index)) +{ + struct NearestFaceUserData_ZBuf *data = userData; + + if (efa == data->face_test) { const float dist_test = len_manhattan_v2v2(data->mval_fl, screen_co); if (dist_test < data->dist) { @@ -545,96 +722,152 @@ static void findnearestface__getDistance(void *userData, BMFace *efa, const floa } } } + + +struct NearestFaceUserData_Hit { + float dist; + float dist_bias; + int index; + BMFace *face; +}; + +struct NearestFaceUserData { + float mval_fl[2]; + bool use_select_bias; + bool use_cycle; + int cycle_index_prev; + + struct NearestFaceUserData_Hit hit; + struct NearestFaceUserData_Hit hit_cycle; +}; + static void findnearestface__doClosest(void *userData, BMFace *efa, const float screen_co[2], int index) { - struct { float mval_fl[2], pass; float dist, lastIndex, closestIndex; BMFace *closest; } *data = userData; + struct NearestFaceUserData *data = userData; + float dist_test, dist_test_bias; - if (data->pass == 0) { - if (index <= data->lastIndex) - return; - } - else { - if (index > data->lastIndex) - return; + dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co); + + if (data->use_select_bias && BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + dist_test_bias += FIND_NEAR_SELECT_BIAS; } - if (data->dist > 3) { - const float dist_test = len_manhattan_v2v2(data->mval_fl, screen_co); + if (dist_test_bias < data->hit.dist_bias) { + data->hit.dist_bias = dist_test_bias; + data->hit.dist = dist_test; + data->hit.index = index; + data->hit.face = efa; + } - if (dist_test < data->dist) { - data->dist = dist_test; - data->closest = efa; - data->closestIndex = index; + if (data->use_cycle) { + if ((data->hit_cycle.face == NULL) && + (index > data->cycle_index_prev) && + (dist_test_bias < FIND_NEAR_CYCLE_THRESHOLD_MIN)) + { + data->hit_cycle.dist_bias = dist_test_bias; + data->hit_cycle.dist = dist_test; + data->hit_cycle.index = index; + data->hit_cycle.face = efa; } } } -BMFace *EDBM_face_find_nearest(ViewContext *vc, float *r_dist) + +BMFace *EDBM_face_find_nearest_ex( + ViewContext *vc, float *r_dist, + float *r_dist_center, + const bool use_select_bias, const bool use_cycle, + BMFace **r_efa_zbuf) { + BMesh *bm = vc->em->bm; - if (vc->v3d->drawtype > OB_WIRE && (vc->v3d->flag & V3D_ZBUF_SELECT)) { + if (V3D_IS_ZBUF(vc->v3d)) { + float dist_test = 0.0f; unsigned int index; BMFace *efa; - view3d_validate_backbuf(vc); + ED_view3d_backbuf_validate(vc); - index = view3d_sample_backbuf(vc, vc->mval[0], vc->mval[1]); - efa = index ? BM_face_at_index_find(vc->em->bm, index - 1) : NULL; + index = ED_view3d_backbuf_sample(vc, vc->mval[0], vc->mval[1]); + efa = index ? BM_face_at_index_find_or_table(bm, index - 1) : NULL; - if (efa) { - struct { float mval_fl[2]; float dist; BMFace *toFace; } data; + if (r_efa_zbuf) { + *r_efa_zbuf = efa; + } + + /* exception for faces (verts don't need this) */ + if (r_dist_center && efa) { + struct NearestFaceUserData_ZBuf data; data.mval_fl[0] = vc->mval[0]; data.mval_fl[1] = vc->mval[1]; data.dist = FLT_MAX; - data.toFace = efa; + data.face_test = efa; ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); - mesh_foreachScreenFace(vc, findnearestface__getDistance, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + mesh_foreachScreenFace(vc, find_nearest_face_center__doZBuf, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + + *r_dist_center = data.dist; + } + /* end exception */ - if ((vc->em->selectmode == SCE_SELECT_FACE) || (data.dist < *r_dist)) { /* only faces, no dist check */ - *r_dist = data.dist; + if (efa) { + if (dist_test < *r_dist) { + *r_dist = dist_test; return efa; } } - return NULL; } else { - struct { float mval_fl[2], pass; float dist, lastIndex, closestIndex; BMFace *closest; } data; - static int lastSelectedIndex = 0; - static BMFace *lastSelected = NULL; + struct NearestFaceUserData data = {{0}}; + const struct NearestFaceUserData_Hit *hit; + const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_DEFAULT; + + static int prev_select_index = 0; + static const BMFace *prev_select_elem = NULL; - if (lastSelected && BM_face_at_index_find(vc->em->bm, lastSelectedIndex) != lastSelected) { - lastSelectedIndex = 0; - lastSelected = NULL; + if ((use_cycle == false) || + (prev_select_elem && (prev_select_elem != BM_face_at_index_find_or_table(bm, prev_select_index)))) + { + prev_select_index = 0; + prev_select_elem = NULL; } - data.lastIndex = lastSelectedIndex; data.mval_fl[0] = vc->mval[0]; data.mval_fl[1] = vc->mval[1]; - data.dist = *r_dist; - data.closest = NULL; - data.closestIndex = 0; + data.use_select_bias = use_select_bias; + data.use_cycle = use_cycle; + data.hit.dist = data.hit_cycle.dist = \ + data.hit.dist_bias = data.hit_cycle.dist_bias = *r_dist; + data.cycle_index_prev = prev_select_index; - data.pass = 0; ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); - mesh_foreachScreenFace(vc, findnearestface__doClosest, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + mesh_foreachScreenFace(vc, findnearestface__doClosest, &data, clip_flag); - if (data.dist > 3.0f) { - data.pass = 1; - mesh_foreachScreenFace(vc, findnearestface__doClosest, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + hit = (data.use_cycle && data.hit_cycle.face) ? &data.hit_cycle : &data.hit; + *r_dist = hit->dist; + if (r_dist_center) { + *r_dist_center = hit->dist; } - *r_dist = data.dist; - lastSelected = data.closest; - lastSelectedIndex = data.closestIndex; + prev_select_elem = hit->face; + prev_select_index = hit->index; - return data.closest; + return hit->face; } } +BMFace *EDBM_face_find_nearest(ViewContext *vc, float *r_dist) +{ + return EDBM_face_find_nearest_ex(vc, r_dist, NULL, false, false, NULL); +} + +#undef FIND_NEAR_SELECT_BIAS +#undef FIND_NEAR_CYCLE_THRESHOLD_MIN + + /* best distance based on screen coords. * use em->selectmode to define how to use * selected vertices and edges get disadvantage @@ -643,35 +876,78 @@ BMFace *EDBM_face_find_nearest(ViewContext *vc, float *r_dist) static int unified_findnearest(ViewContext *vc, BMVert **r_eve, BMEdge **r_eed, BMFace **r_efa) { BMEditMesh *em = vc->em; - float dist = ED_view3d_select_dist_px(); - - *r_eve = NULL; - *r_eed = NULL; - *r_efa = NULL; + static short mval_prev[2] = {-1, -1}; + /* only cycle while the mouse remains still */ + const bool use_cycle = ((mval_prev[0] == vc->mval[0]) && (mval_prev[1] == vc->mval[1])); + const float dist_init = ED_view3d_select_dist_px(); + /* since edges select lines, we give dots advantage of ~20 pix */ + const float dist_margin = (dist_init / 2); + float dist = dist_init; + BMFace *efa_zbuf = NULL; + BMEdge *eed_zbuf = NULL; + BMVert *eve = NULL; + BMEdge *eed = NULL; + BMFace *efa = NULL; + + /* no afterqueue (yet), so we check it now, otherwise the em_xxxofs indices are bad */ - view3d_validate_backbuf(vc); - - if (em->selectmode & SCE_SELECT_VERTEX) - *r_eve = EDBM_vert_find_nearest(vc, &dist, BM_ELEM_SELECT, 0); - if (em->selectmode & SCE_SELECT_FACE) - *r_efa = EDBM_face_find_nearest(vc, &dist); + ED_view3d_backbuf_validate(vc); - dist -= 20; /* since edges select lines, we give dots advantage of 20 pix */ - if (em->selectmode & SCE_SELECT_EDGE) - *r_eed = EDBM_edge_find_nearest(vc, &dist); + if ((dist > 0.0f) && em->selectmode & SCE_SELECT_FACE) { + float dist_center = 0.0f; + float *dist_center_p = (em->selectmode & (SCE_SELECT_EDGE | SCE_SELECT_VERTEX)) ? &dist_center : NULL; + efa = EDBM_face_find_nearest_ex(vc, &dist, dist_center_p, true, use_cycle, &efa_zbuf); + if (efa && dist_center_p) { + dist = min_ff(dist_margin, dist_center); + } + } + + if ((dist > 0.0f) && (em->selectmode & SCE_SELECT_EDGE)) { + float dist_center = 0.0f; + float *dist_center_p = (em->selectmode & SCE_SELECT_VERTEX) ? &dist_center : NULL; + eed = EDBM_edge_find_nearest_ex(vc, &dist, dist_center_p, true, use_cycle, &eed_zbuf); + if (eed && dist_center_p) { + dist = min_ff(dist_margin, dist_center); + } + } + + if ((dist > 0.0f) && em->selectmode & SCE_SELECT_VERTEX) { + eve = EDBM_vert_find_nearest_ex(vc, &dist, true, use_cycle); + } /* return only one of 3 pointers, for frontbuffer redraws */ - if (*r_eed) { - *r_efa = NULL; *r_eve = NULL; + if (eve) { + efa = NULL; eed = NULL; } - else if (*r_efa) { - *r_eve = NULL; + else if (eed) { + efa = NULL; } - - return (*r_eve || *r_eed || *r_efa); + + /* there may be a face under the cursor, who's center if too far away + * use this if all else fails, it makes sense to select this */ + if ((eve || eed || efa) == 0) { + if (eed_zbuf) { + eed = eed_zbuf; + } + else if (efa_zbuf) { + efa = efa_zbuf; + } + } + + mval_prev[0] = vc->mval[0]; + mval_prev[1] = vc->mval[1]; + + *r_eve = eve; + *r_eed = eed; + *r_efa = efa; + + return (eve || eed || efa); } +/** \} */ + + /* **************** SIMILAR "group" SELECTS. FACE, EDGE AND VERTEX ************** */ static EnumPropertyItem prop_similar_compare_types[] = { {SIM_CMP_EQ, "EQUAL", 0, "Equal", ""}, @@ -1179,7 +1455,7 @@ static int edbm_loop_multiselect_exec(bContext *C, wmOperator *op) else { for (edindex = 0; edindex < totedgesel; edindex += 1) { eed = edarray[edindex]; - walker_select(em, BMW_LOOP, eed, true); + walker_select(em, BMW_EDGELOOP, eed, true); } EDBM_selectmode_flush(em); } @@ -1237,12 +1513,12 @@ static void mouse_mesh_loop_edge(BMEditMesh *em, BMEdge *eed, bool select, bool { bool edge_boundary = false; - /* cycle between BMW_LOOP / BMW_EDGEBOUNDARY */ + /* cycle between BMW_EDGELOOP / BMW_EDGEBOUNDARY */ if (select_cycle && BM_edge_is_boundary(eed)) { int tot[2]; /* if the loops selected toggle the boundaries */ - walker_select_count(em, BMW_LOOP, eed, select, false, + walker_select_count(em, BMW_EDGELOOP, eed, select, false, &tot[0], &tot[1]); if (tot[select] == 0) { edge_boundary = true; @@ -1264,7 +1540,7 @@ static void mouse_mesh_loop_edge(BMEditMesh *em, BMEdge *eed, bool select, bool walker_select(em, BMW_EDGEBOUNDARY, eed, select); } else { - walker_select(em, BMW_LOOP, eed, select); + walker_select(em, BMW_EDGELOOP, eed, select); } } @@ -1286,7 +1562,7 @@ static bool mouse_mesh_loop(bContext *C, const int mval[2], bool extend, bool de em = vc.em; /* no afterqueue (yet), so we check it now, otherwise the bm_xxxofs indices are bad */ - view3d_validate_backbuf(&vc); + ED_view3d_backbuf_validate(&vc); eed = EDBM_edge_find_nearest(&vc, &dist); if (eed == NULL) { @@ -1307,16 +1583,8 @@ static bool mouse_mesh_loop(bContext *C, const int mval[2], bool extend, bool de select = true; } else if (toggle) { - int tot[2]; - /* Shift-clicking to toggle selection and shift-clicking an already selected edge - * loop to do boundary selection conflicts here (see T43871), so we only toggle/deselect - * if the entire boundary is selected. This gives the following behaviour for shift - * clicking an edge loop: select edge loop, select edge boundary, toggle edge loop */ - walker_select_count(em, BMW_EDGEBOUNDARY, eed, select, false, &tot[0], &tot[1]); - if (tot[select] == 0) { - select = false; - select_cycle = false; - } + select = false; + select_cycle = false; } if (em->selectmode & SCE_SELECT_FACE) { @@ -2050,7 +2318,7 @@ bool EDBM_select_interior_faces(BMEditMesh *em) ok = true; BM_ITER_ELEM (eed, &eiter, efa, BM_EDGES_OF_FACE) { - if (BM_edge_face_count(eed) < 3) { + if (!BM_edge_face_count_is_over(eed, 2)) { ok = false; break; } @@ -2068,16 +2336,98 @@ bool EDBM_select_interior_faces(BMEditMesh *em) /************************ Select Linked Operator *************************/ -static void linked_limit_default(bContext *C, wmOperator *op) +struct DelimitData { + int cd_loop_type; + int cd_loop_offset; +}; + +static bool select_linked_delimit_test( + BMEdge *e, int delimit, + const struct DelimitData *delimit_data) { - if (!RNA_struct_property_is_set(op->ptr, "limit")) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - if (em->selectmode == SCE_SELECT_FACE) - RNA_boolean_set(op->ptr, "limit", true); - else - RNA_boolean_set(op->ptr, "limit", false); + BLI_assert(delimit); + + if (delimit & BMO_DELIM_SEAM) { + if (BM_elem_flag_test(e, BM_ELEM_SEAM)) { + return true; + } } + + if (delimit & BMO_DELIM_SHARP) { + if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) == 0) { + return true; + } + } + + if (delimit & BMO_DELIM_NORMAL) { + if (!BM_edge_is_contiguous(e)) { + return true; + } + } + + if (delimit & BMO_DELIM_MATERIAL) { + if (e->l && e->l->radial_next != e->l) { + const short mat_nr = e->l->f->mat_nr; + BMLoop *l_iter = e->l->radial_next; + do { + if (l_iter->f->mat_nr != mat_nr) { + return true; + } + } while ((l_iter = l_iter->radial_next) != e->l); + } + } + + if (delimit & BMO_DELIM_UV) { + if (BM_edge_is_contiguous_loop_cd(e, delimit_data->cd_loop_type, delimit_data->cd_loop_offset) == 0) { + return true; + } + } + + return false; +} + +static void select_linked_delimit_begin(BMesh *bm, short selectmode, int delimit) +{ + struct DelimitData delimit_data = {0}; + + BMIter iter; + BMEdge *e; + + if (delimit & BMO_DELIM_UV) { + delimit_data.cd_loop_type = CD_MLOOPUV; + delimit_data.cd_loop_offset = CustomData_get_offset(&bm->ldata, delimit_data.cd_loop_type); + if (delimit_data.cd_loop_offset == -1) { + delimit &= ~BMO_DELIM_UV; + } + } + + /* grr, shouldn't need to alloc BMO flags here */ + BM_mesh_elem_toolflags_ensure(bm); + if (selectmode == SCE_SELECT_FACE) { + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + const bool is_walk_ok = ( + (select_linked_delimit_test(e, delimit, &delimit_data) == false)); + + BMO_elem_flag_set(bm, e, BMO_ELE_TAG, is_walk_ok); + } + } + else { + /* don't delimit selected edges in vert/edge mode */ + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + const bool is_walk_ok = ( + BM_elem_flag_test(e, BM_ELEM_SELECT) || + (select_linked_delimit_test(e, delimit, &delimit_data) == false)); + + BMO_elem_flag_set(bm, e, BMO_ELE_TAG, is_walk_ok); + } + } +} + +static void select_linked_delimit_end(BMEditMesh *em) +{ + BMesh *bm = em->bm; + + BM_mesh_elem_toolflags_clear(bm); } static int edbm_select_linked_exec(bContext *C, wmOperator *op) @@ -2086,72 +2436,139 @@ static int edbm_select_linked_exec(bContext *C, wmOperator *op) BMEditMesh *em = BKE_editmesh_from_object(obedit); BMesh *bm = em->bm; BMIter iter; - BMEdge *e; BMWalker walker; - int limit; - - linked_limit_default(C, op); + const int delimit = RNA_enum_get(op->ptr, "delimit"); - limit = RNA_boolean_get(op->ptr, "limit"); + if (delimit) { + select_linked_delimit_begin(em->bm, em->selectmode, delimit); + } - if (em->selectmode == SCE_SELECT_FACE) { - BMFace *efa; + if (em->selectmode & SCE_SELECT_VERTEX) { + BMVert *v; - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - BM_elem_flag_set(efa, BM_ELEM_TAG, BM_elem_flag_test(efa, BM_ELEM_SELECT)); + BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) { + BM_elem_flag_set(v, BM_ELEM_TAG, BM_elem_flag_test(v, BM_ELEM_SELECT)); } - if (limit) { - /* grr, shouldn't need to alloc BMO flags here */ - BM_mesh_elem_toolflags_ensure(bm); - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - BMO_elem_flag_set(bm, e, BMO_ELE_TAG, !BM_elem_flag_test(e, BM_ELEM_SEAM)); + BMW_init(&walker, em->bm, delimit ? BMW_LOOP_SHELL_WIRE : BMW_VERT_SHELL, + BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP, + BMW_FLAG_TEST_HIDDEN, + BMW_NIL_LAY); + + if (delimit) { + BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, BM_ELEM_TAG)) { + BMElem *ele_walk; + BMW_ITER (ele_walk, &walker, v) { + if (ele_walk->head.htype == BM_LOOP) { + BMVert *v_step = ((BMLoop *)ele_walk)->v; + BM_vert_select_set(em->bm, v_step, true); + BM_elem_flag_disable(v_step, BM_ELEM_TAG); + } + else { + BMEdge *e_step = (BMEdge *)ele_walk; + BLI_assert(ele_walk->head.htype == BM_EDGE); + BM_edge_select_set(em->bm, e_step, true); + BM_elem_flag_disable(e_step->v1, BM_ELEM_TAG); + BM_elem_flag_disable(e_step->v2, BM_ELEM_TAG); + } + } + } + } + } + else { + BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, BM_ELEM_TAG)) { + BMEdge *e_walk; + BMW_ITER (e_walk, &walker, v) { + BM_edge_select_set(em->bm, e_walk, true); + BM_elem_flag_disable(e_walk, BM_ELEM_TAG); + } + } } } - BMW_init(&walker, bm, BMW_ISLAND, - BMW_MASK_NOP, limit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP, + BMW_end(&walker); + + EDBM_selectmode_flush(em); + } + else if (em->selectmode & SCE_SELECT_EDGE) { + BMEdge *e; + + BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { + BM_elem_flag_set(e, BM_ELEM_TAG, BM_elem_flag_test(e, BM_ELEM_SELECT)); + } + + BMW_init(&walker, em->bm, delimit ? BMW_LOOP_SHELL_WIRE : BMW_VERT_SHELL, + BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP, BMW_FLAG_TEST_HIDDEN, BMW_NIL_LAY); - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(efa, BM_ELEM_TAG)) { - for (efa = BMW_begin(&walker, efa); efa; efa = BMW_step(&walker)) { - BM_face_select_set(bm, efa, true); - BM_elem_flag_disable(efa, BM_ELEM_TAG); + if (delimit) { + BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, BM_ELEM_TAG)) { + BMElem *ele_walk; + BMW_ITER (ele_walk, &walker, e) { + if (ele_walk->head.htype == BM_LOOP) { + BMLoop *l_step = (BMLoop *)ele_walk; + BM_edge_select_set(em->bm, l_step->e, true); + BM_edge_select_set(em->bm, l_step->prev->e, true); + BM_elem_flag_disable(l_step->e, BM_ELEM_TAG); + } + else { + BMEdge *e_step = (BMEdge *)ele_walk; + BLI_assert(ele_walk->head.htype == BM_EDGE); + BM_edge_select_set(em->bm, e_step, true); + BM_elem_flag_disable(e_step, BM_ELEM_TAG); + } + } } } } + else { + BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, BM_ELEM_TAG)) { + BMEdge *e_walk; + BMW_ITER (e_walk, &walker, e) { + BM_edge_select_set(em->bm, e_walk, true); + BM_elem_flag_disable(e_walk, BM_ELEM_TAG); + } + } + } + } + BMW_end(&walker); - if (limit) { - BM_mesh_elem_toolflags_clear(bm); - } + EDBM_selectmode_flush(em); } else { - BMVert *v; + BMFace *f; - BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) { - BM_elem_flag_set(v, BM_ELEM_TAG, BM_elem_flag_test(v, BM_ELEM_SELECT)); + BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) { + BM_elem_flag_set(f, BM_ELEM_TAG, BM_elem_flag_test(f, BM_ELEM_SELECT)); } - BMW_init(&walker, em->bm, BMW_VERT_SHELL, - BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, + BMW_init(&walker, bm, BMW_ISLAND, + BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP, BMW_FLAG_TEST_HIDDEN, BMW_NIL_LAY); - BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(v, BM_ELEM_TAG)) { - for (e = BMW_begin(&walker, v); e; e = BMW_step(&walker)) { - BM_edge_select_set(em->bm, e, true); - BM_elem_flag_disable(e, BM_ELEM_TAG); + BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_TAG)) { + BMFace *f_walk; + BMW_ITER (f_walk, &walker, f) { + BM_face_select_set(bm, f_walk, true); + BM_elem_flag_disable(f_walk, BM_ELEM_TAG); } } } + BMW_end(&walker); + } - EDBM_selectmode_flush(em); + if (delimit) { + select_linked_delimit_end(em); } WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit); @@ -2173,99 +2590,209 @@ void MESH_OT_select_linked(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "limit", 0, "Limit by Seams", ""); + RNA_def_enum_flag(ot->srna, "delimit", mesh_delimit_mode_items, BMO_DELIM_SEAM, "Delimit", + "Delimit selected region"); +} + +static int edbm_select_linked_pick_exec(bContext *C, wmOperator *op); + +static void edbm_select_linked_pick_ex( + BMEditMesh *em, + BMVert *eve, BMEdge *eed, BMFace *efa, + bool sel, int delimit) +{ + BMesh *bm = em->bm; + BMWalker walker; + + if (delimit) { + select_linked_delimit_begin(bm, em->selectmode, delimit); + } + + /* Note: logic closely matches 'edbm_select_linked_exec', keep in sync */ + + if ((em->selectmode & SCE_SELECT_VERTEX) && eve) { + + BMW_init(&walker, bm, delimit ? BMW_LOOP_SHELL_WIRE : BMW_VERT_SHELL, + BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP, + BMW_FLAG_TEST_HIDDEN, + BMW_NIL_LAY); + + if (delimit) { + BMElem *ele_walk; + BMW_ITER (ele_walk, &walker, eve) { + if (ele_walk->head.htype == BM_LOOP) { + BMVert *v_step = ((BMLoop *)ele_walk)->v; + BM_vert_select_set(bm, v_step, sel); + } + else { + BMEdge *e_step = (BMEdge *)ele_walk; + BLI_assert(ele_walk->head.htype == BM_EDGE); + BM_edge_select_set(bm, e_step, sel); + } + } + } + else { + BMEdge *e_walk; + BMW_ITER (e_walk, &walker, eve) { + BM_edge_select_set(bm, e_walk, sel); + } + } + + BMW_end(&walker); + + EDBM_selectmode_flush(em); + } + else if ((em->selectmode & SCE_SELECT_EDGE) && eed) { + + BMW_init(&walker, bm, delimit ? BMW_LOOP_SHELL_WIRE : BMW_VERT_SHELL, + BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP, + BMW_FLAG_TEST_HIDDEN, + BMW_NIL_LAY); + + if (delimit) { + BMElem *ele_walk; + BMW_ITER (ele_walk, &walker, eed) { + if (ele_walk->head.htype == BM_LOOP) { + BMEdge *e_step = ((BMLoop *)ele_walk)->e; + BM_edge_select_set(bm, e_step, sel); + } + else { + BMEdge *e_step = (BMEdge *)ele_walk; + BLI_assert(ele_walk->head.htype == BM_EDGE); + BM_edge_select_set(bm, e_step, sel); + } + } + } + else { + BMEdge *e_walk; + BMW_ITER (e_walk, &walker, eed) { + BM_edge_select_set(bm, e_walk, sel); + } + } + + BMW_end(&walker); + + EDBM_selectmode_flush(em); + } + else if ((em->selectmode & SCE_SELECT_FACE) && efa) { + + BMW_init(&walker, bm, BMW_ISLAND, + BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP, + BMW_FLAG_TEST_HIDDEN, + BMW_NIL_LAY); + + { + BMFace *f_walk; + BMW_ITER (f_walk, &walker, efa) { + BM_face_select_set(bm, f_walk, sel); + BM_elem_flag_disable(f_walk, BM_ELEM_TAG); + } + } + + BMW_end(&walker); + } + + if (delimit) { + select_linked_delimit_end(em); + } } static int edbm_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event) { Object *obedit = CTX_data_edit_object(C); ViewContext vc; - BMesh *bm; - BMWalker walker; BMEditMesh *em; + BMesh *bm; BMVert *eve; - BMEdge *e, *eed; + BMEdge *eed; BMFace *efa; const bool sel = !RNA_boolean_get(op->ptr, "deselect"); + const int delimit = RNA_enum_get(op->ptr, "delimit"); + int index; - int limit; - - linked_limit_default(C, op); - - limit = RNA_boolean_get(op->ptr, "limit"); + if (RNA_struct_property_is_set(op->ptr, "index")) { + return edbm_select_linked_pick_exec(C, op); + } /* unified_finednearest needs ogl */ view3d_operator_needs_opengl(C); - + /* setup view context for argument to callbacks */ em_setup_viewcontext(C, &vc); em = vc.em; + bm = em->bm; - if (em->bm->totedge == 0) + if (bm->totedge == 0) { return OPERATOR_CANCELLED; - - bm = em->bm; + } vc.mval[0] = event->mval[0]; vc.mval[1] = event->mval[1]; - + /* return warning! */ - if (unified_findnearest(&vc, &eve, &eed, &efa) == 0) { WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit); - + return OPERATOR_CANCELLED; } - - if (em->selectmode == SCE_SELECT_FACE) { - BMIter iter; - - if (efa == NULL) - return OPERATOR_CANCELLED; - - if (limit) { - /* grr, shouldn't need to alloc BMO flags here */ - BM_mesh_elem_toolflags_ensure(bm); - /* hflag no-seam --> bmo-tag */ - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - BMO_elem_flag_set(bm, e, BMO_ELE_TAG, !BM_elem_flag_test(e, BM_ELEM_SEAM)); - } - } - /* walk */ - BMW_init(&walker, bm, BMW_ISLAND, - BMW_MASK_NOP, limit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP, - BMW_FLAG_TEST_HIDDEN, - BMW_NIL_LAY); + edbm_select_linked_pick_ex(em, eve, eed, efa, sel, delimit); - for (efa = BMW_begin(&walker, efa); efa; efa = BMW_step(&walker)) { - BM_face_select_set(bm, efa, sel); - } - BMW_end(&walker); + /* to support redo */ + if ((em->selectmode & SCE_SELECT_VERTEX) && eve) { + BM_mesh_elem_index_ensure(bm, BM_VERT); + index = BM_elem_index_get(eve); + } + else if ((em->selectmode & SCE_SELECT_EDGE) && eed) { + BM_mesh_elem_index_ensure(bm, BM_EDGE); + index = BM_elem_index_get(eed) + bm->totvert; + } + else if ((em->selectmode & SCE_SELECT_FACE) && efa) { + BM_mesh_elem_index_ensure(bm, BM_FACE); + index = BM_elem_index_get(efa) + bm->totvert + bm->totedge; } else { - if (efa) { - eed = BM_FACE_FIRST_LOOP(efa)->e; - } - else if (!eed) { - if (!eve || !eve->e) - return OPERATOR_CANCELLED; + index = -1; + } - eed = eve->e; - } + RNA_int_set(op->ptr, "index", index); - BMW_init(&walker, bm, BMW_VERT_SHELL, - BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, - BMW_FLAG_TEST_HIDDEN, - BMW_NIL_LAY); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit); - for (e = BMW_begin(&walker, eed->v1); e; e = BMW_step(&walker)) { - BM_edge_select_set(bm, e, sel); - } - BMW_end(&walker); + return OPERATOR_FINISHED; +} - EDBM_selectmode_flush(em); + +static int edbm_select_linked_pick_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + int index; + BMVert *eve = NULL; + BMEdge *eed = NULL; + BMFace *efa = NULL; + const bool sel = !RNA_boolean_get(op->ptr, "deselect"); + const int delimit = RNA_enum_get(op->ptr, "delimit"); + + index = RNA_int_get(op->ptr, "index"); + if (index < 0 || index >= (bm->totvert + bm->totedge + bm->totface)) { + return OPERATOR_CANCELLED; + } + + if (index < bm->totvert) { + eve = BM_vert_at_index_find_or_table(bm, index); } + else if (index < (bm->totvert + bm->totedge)) { + index -= bm->totvert; + eed = BM_edge_at_index_find_or_table(bm, index); + } + else if (index < (bm->totvert + bm->totedge + bm->totface)) { + index -= (bm->totvert + bm->totedge); + efa = BM_face_at_index_find_or_table(bm, index); + } + + edbm_select_linked_pick_ex(em, eve, eed, efa, sel, delimit); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit); @@ -2274,6 +2801,8 @@ static int edbm_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmE void MESH_OT_select_linked_pick(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Select Linked"; ot->idname = "MESH_OT_select_linked_pick"; @@ -2281,13 +2810,19 @@ void MESH_OT_select_linked_pick(wmOperatorType *ot) /* api callbacks */ ot->invoke = edbm_select_linked_pick_invoke; + ot->exec = edbm_select_linked_pick_exec; ot->poll = ED_operator_editmesh; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", ""); - RNA_def_boolean(ot->srna, "limit", 0, "Limit by Seams", ""); + RNA_def_enum_flag(ot->srna, "delimit", mesh_delimit_mode_items, BMO_DELIM_SEAM, "Delimit", + "Delimit selected region"); + + /* use for redo */ + prop = RNA_def_int(ot->srna, "index", -1, 0, INT_MAX, "", "", 0, INT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } @@ -2560,7 +3095,7 @@ static bool bm_edge_is_select_isolated(BMEdge *e) /* Walk all reachable elements of the same type as h_act in breadth-first * order, starting from h_act. Deselects elements if the depth when they * are reached is not a multiple of "nth". */ -static void walker_deselect_nth(BMEditMesh *em, int nth, int offset, BMHeader *h_act) +static void walker_deselect_nth(BMEditMesh *em, int nth, int skip, int offset, BMHeader *h_act) { BMElem *ele; BMesh *bm = em->bm; @@ -2626,7 +3161,8 @@ static void walker_deselect_nth(BMEditMesh *em, int nth, int offset, BMHeader *h for (ele = BMW_begin(&walker, h_act); ele != NULL; ele = BMW_step(&walker)) { if (!BM_elem_flag_test(ele, BM_ELEM_TAG)) { /* Deselect elements that aren't at "nth" depth from active */ - if ((offset + BMW_current_depth(&walker)) % nth) { + const int depth = BMW_current_depth(&walker) - 1; + if ((offset + depth) % (skip + nth) >= skip) { BM_elem_select_set(bm, ele, false); } BM_elem_flag_enable(ele, BM_ELEM_TAG); @@ -2693,7 +3229,7 @@ static void deselect_nth_active(BMEditMesh *em, BMVert **r_eve, BMEdge **r_eed, } } -static bool edbm_deselect_nth(BMEditMesh *em, int nth, int offset) +static bool edbm_deselect_nth(BMEditMesh *em, int nth, int skip, int offset) { BMVert *v; BMEdge *e; @@ -2702,15 +3238,15 @@ static bool edbm_deselect_nth(BMEditMesh *em, int nth, int offset) deselect_nth_active(em, &v, &e, &f); if (v) { - walker_deselect_nth(em, nth, offset, &v->head); + walker_deselect_nth(em, nth, skip, offset, &v->head); return true; } else if (e) { - walker_deselect_nth(em, nth, offset, &e->head); + walker_deselect_nth(em, nth, skip, offset, &e->head); return true; } else if (f) { - walker_deselect_nth(em, nth, offset, &f->head); + walker_deselect_nth(em, nth, skip, offset, &f->head); return true; } @@ -2721,15 +3257,14 @@ static int edbm_select_nth_exec(bContext *C, wmOperator *op) { Object *obedit = CTX_data_edit_object(C); BMEditMesh *em = BKE_editmesh_from_object(obedit); - const int nth = RNA_int_get(op->ptr, "nth"); + const int nth = RNA_int_get(op->ptr, "nth") - 1; + const int skip = RNA_int_get(op->ptr, "skip"); int offset = RNA_int_get(op->ptr, "offset"); /* so input of offset zero ends up being (nth - 1) */ - offset = mod_i(offset, nth); - /* depth starts at 1, this keeps active item selected */ - offset -= 1; + offset = mod_i(offset, nth + skip); - if (edbm_deselect_nth(em, nth, offset) == false) { + if (edbm_deselect_nth(em, nth, skip, offset) == false) { BKE_report(op->reports, RPT_ERROR, "Mesh has no active vert/edge/face"); return OPERATOR_CANCELLED; } @@ -2755,6 +3290,7 @@ void MESH_OT_select_nth(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; RNA_def_int(ot->srna, "nth", 2, 2, INT_MAX, "Nth Selection", "", 2, 100); + RNA_def_int(ot->srna, "skip", 1, 1, INT_MAX, "Skip", "", 1, 100); RNA_def_int(ot->srna, "offset", 0, INT_MIN, INT_MAX, "Offset", "", -100, 100); } @@ -2945,7 +3481,7 @@ static int edbm_select_non_manifold_exec(bContext *C, wmOperator *op) if ((use_wire && BM_edge_is_wire(e)) || (use_boundary && BM_edge_is_boundary(e)) || (use_non_contiguous && (BM_edge_is_manifold(e) && !BM_edge_is_contiguous(e))) || - (use_multi_face && (BM_edge_face_count(e) > 2))) + (use_multi_face && (BM_edge_face_count_is_over(e, 2)))) { /* check we never select perfect edge (in test above) */ BLI_assert(!(BM_edge_is_manifold(e) && BM_edge_is_contiguous(e))); @@ -3144,7 +3680,7 @@ static int edbm_select_axis_exec(bContext *C, wmOperator *op) else { BMVert *v; BMIter iter; - const float limit = CTX_data_tool_settings(C)->doublimit; // XXX + const float limit = RNA_float_get(op->ptr, "threshold"); float value = v_act->co[axis]; if (mode == 0) @@ -3209,6 +3745,7 @@ void MESH_OT_select_axis(wmOperatorType *ot) /* properties */ RNA_def_enum(ot->srna, "mode", axis_mode_items, 0, "Axis Mode", "Axis side to use when selecting"); RNA_def_enum(ot->srna, "axis", axis_items_xyz, 0, "Axis", "Select the axis to compare each vertex on"); + RNA_def_float(ot->srna, "threshold", 0.0001f, 0.000001f, 50.0f, "Threshold", "", 0.00001, 10.0); } |