From e0a261c653cb158d940569c830f81a0c064da40e Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sat, 1 Oct 2022 22:12:55 -0500 Subject: Cleanup: Move more files using mesh runtime data to C++ In preparation for moving mesh runtime data out of DNA. --- source/blender/editors/mesh/editmesh_select.c | 5344 ------------------------- 1 file changed, 5344 deletions(-) delete mode 100644 source/blender/editors/mesh/editmesh_select.c (limited to 'source/blender/editors/mesh/editmesh_select.c') diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c deleted file mode 100644 index 34f7301c4b1..00000000000 --- a/source/blender/editors/mesh/editmesh_select.c +++ /dev/null @@ -1,5344 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2004 Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup edmesh - */ - -#include "MEM_guardedalloc.h" - -#include "BLI_array.h" -#include "BLI_bitmap.h" -#include "BLI_heap.h" -#include "BLI_linklist.h" -#include "BLI_linklist_stack.h" -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_math_bits.h" -#include "BLI_rand.h" -#include "BLI_string.h" -#include "BLI_utildefines_stack.h" - -#include "BKE_context.h" -#include "BKE_customdata.h" -#include "BKE_deform.h" -#include "BKE_editmesh.h" -#include "BKE_layer.h" -#include "BKE_report.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "RNA_access.h" -#include "RNA_define.h" -#include "RNA_enum_types.h" - -#include "ED_mesh.h" -#include "ED_object.h" -#include "ED_screen.h" -#include "ED_select_utils.h" -#include "ED_transform.h" -#include "ED_view3d.h" - -#include "BLT_translation.h" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_object_types.h" - -#include "UI_resources.h" - -#include "bmesh_tools.h" - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" - -#include "DRW_select_buffer.h" - -#include "mesh_intern.h" /* own include */ - -/* use bmesh operator flags for a few operators */ -#define BMO_ELE_TAG 1 - -/* -------------------------------------------------------------------- */ -/** \name Select Mirror - * \{ */ - -void EDBM_select_mirrored(BMEditMesh *em, - const Mesh *me, - const int axis, - const bool extend, - int *r_totmirr, - int *r_totfail) -{ - BMesh *bm = em->bm; - BMIter iter; - int totmirr = 0; - int totfail = 0; - bool use_topology = me->editflag & ME_EDIT_MIRROR_TOPO; - - *r_totmirr = *r_totfail = 0; - - /* select -> tag */ - if (bm->selectmode & SCE_SELECT_VERTEX) { - BMVert *v; - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - BM_elem_flag_set(v, BM_ELEM_TAG, BM_elem_flag_test(v, BM_ELEM_SELECT)); - } - } - else if (em->selectmode & SCE_SELECT_EDGE) { - BMEdge *e; - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - BM_elem_flag_set(e, BM_ELEM_TAG, BM_elem_flag_test(e, BM_ELEM_SELECT)); - } - } - else { - BMFace *f; - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - BM_elem_flag_set(f, BM_ELEM_TAG, BM_elem_flag_test(f, BM_ELEM_SELECT)); - } - } - - EDBM_verts_mirror_cache_begin(em, axis, true, true, false, use_topology); - - if (!extend) { - EDBM_flag_disable_all(em, BM_ELEM_SELECT); - } - - if (bm->selectmode & SCE_SELECT_VERTEX) { - BMVert *v; - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN) && BM_elem_flag_test(v, BM_ELEM_TAG)) { - BMVert *v_mirr = EDBM_verts_mirror_get(em, v); - if (v_mirr && !BM_elem_flag_test(v_mirr, BM_ELEM_HIDDEN)) { - BM_vert_select_set(bm, v_mirr, true); - totmirr++; - } - else { - totfail++; - } - } - } - } - else if (em->selectmode & SCE_SELECT_EDGE) { - BMEdge *e; - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && BM_elem_flag_test(e, BM_ELEM_TAG)) { - BMEdge *e_mirr = EDBM_verts_mirror_get_edge(em, e); - if (e_mirr && !BM_elem_flag_test(e_mirr, BM_ELEM_HIDDEN)) { - BM_edge_select_set(bm, e_mirr, true); - totmirr++; - } - else { - totfail++; - } - } - } - } - else { - BMFace *f; - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN) && BM_elem_flag_test(f, BM_ELEM_TAG)) { - BMFace *f_mirr = EDBM_verts_mirror_get_face(em, f); - if (f_mirr && !BM_elem_flag_test(f_mirr, BM_ELEM_HIDDEN)) { - BM_face_select_set(bm, f_mirr, true); - totmirr++; - } - else { - totfail++; - } - } - } - } - - EDBM_verts_mirror_cache_end(em); - - *r_totmirr = totmirr; - *r_totfail = totfail; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Back-Buffer OpenGL Selection - * \{ */ - -static BMElem *edbm_select_id_bm_elem_get(Base **bases, const uint sel_id, uint *r_base_index) -{ - uint elem_id; - char elem_type = 0; - bool success = DRW_select_buffer_elem_get(sel_id, &elem_id, r_base_index, &elem_type); - - if (success) { - Object *obedit = bases[*r_base_index]->object; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - - switch (elem_type) { - case SCE_SELECT_FACE: - return (BMElem *)BM_face_at_index_find_or_table(em->bm, elem_id); - case SCE_SELECT_EDGE: - return (BMElem *)BM_edge_at_index_find_or_table(em->bm, elem_id); - case SCE_SELECT_VERTEX: - return (BMElem *)BM_vert_at_index_find_or_table(em->bm, elem_id); - default: - BLI_assert(0); - return NULL; - } - } - - return NULL; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Find Nearest Vert/Edge/Face - * - * \note Screen-space manhattan 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 NearestVertUserData *data = userData; - float dist_test, dist_test_bias; - - 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 (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 (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; - } - } -} - -BMVert *EDBM_vert_find_nearest_ex(ViewContext *vc, - float *dist_px_manhattan_p, - const bool use_select_bias, - bool use_cycle, - Base **bases, - uint bases_len, - uint *r_base_index) -{ - uint base_index = 0; - - if (!XRAY_FLAG_ENABLED(vc->v3d)) { - uint dist_px_manhattan_test = (uint)ED_view3d_backbuf_sample_size_clamp(vc->region, - *dist_px_manhattan_p); - uint index; - BMVert *eve; - - /* No afterqueue (yet), so we check it now, otherwise the bm_xxxofs indices are bad. */ - { - DRW_select_buffer_context_create(bases, bases_len, SCE_SELECT_VERTEX); - - index = DRW_select_buffer_find_nearest_to_point( - vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px_manhattan_test); - - if (index) { - eve = (BMVert *)edbm_select_id_bm_elem_get(bases, index, &base_index); - } - else { - eve = NULL; - } - } - - if (eve) { - if (dist_px_manhattan_test < *dist_px_manhattan_p) { - if (r_base_index) { - *r_base_index = base_index; - } - *dist_px_manhattan_p = dist_px_manhattan_test; - return eve; - } - } - return NULL; - } - - struct NearestVertUserData data = {{0}}; - const struct NearestVertUserData_Hit *hit = NULL; - const eV3DProjTest clip_flag = RV3D_CLIPPING_ENABLED(vc->v3d, vc->rv3d) ? - V3D_PROJ_TEST_CLIP_DEFAULT : - V3D_PROJ_TEST_CLIP_DEFAULT & ~V3D_PROJ_TEST_CLIP_BB; - BMesh *prev_select_bm = NULL; - - static struct { - int index; - const BMVert *elem; - const BMesh *bm; - } prev_select = {0}; - - data.mval_fl[0] = vc->mval[0]; - data.mval_fl[1] = vc->mval[1]; - data.use_select_bias = use_select_bias; - data.use_cycle = use_cycle; - - for (; base_index < bases_len; base_index++) { - Base *base_iter = bases[base_index]; - ED_view3d_viewcontext_init_object(vc, base_iter->object); - if (use_cycle && prev_select.bm == vc->em->bm && - prev_select.elem == BM_vert_at_index_find_or_table(vc->em->bm, prev_select.index)) { - data.cycle_index_prev = prev_select.index; - /* No need to compare in the rest of the loop. */ - use_cycle = false; - } - else { - data.cycle_index_prev = 0; - } - - data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias = - *dist_px_manhattan_p; - - ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); - mesh_foreachScreenVert(vc, findnearestvert__doClosest, &data, clip_flag); - - hit = (data.use_cycle && data.hit_cycle.vert) ? &data.hit_cycle : &data.hit; - - if (hit->dist < *dist_px_manhattan_p) { - if (r_base_index) { - *r_base_index = base_index; - } - *dist_px_manhattan_p = hit->dist; - prev_select_bm = vc->em->bm; - } - } - - if (hit == NULL) { - return NULL; - } - - prev_select.index = hit->index; - prev_select.elem = hit->vert; - prev_select.bm = prev_select_bm; - - return hit->vert; -} - -BMVert *EDBM_vert_find_nearest(ViewContext *vc, float *dist_px_manhattan_p) -{ - BKE_view_layer_synced_ensure(vc->scene, vc->view_layer); - Base *base = BKE_view_layer_base_find(vc->view_layer, vc->obact); - return EDBM_vert_find_nearest_ex(vc, dist_px_manhattan_p, false, false, &base, 1, NULL); -} - -/* 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 manhattan distance to which ever edge we pick - * (not used for choosing) */ - float dist_center_px_manhattan; -}; - -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 find_nearest_edge__doClosest( - void *userData, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index) -{ - struct NearestEdgeUserData *data = userData; - float dist_test, dist_test_bias; - - 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); - } - - dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co); - - if (data->use_select_bias && BM_elem_flag_test(eed, BM_ELEM_SELECT)) { - dist_test_bias += FIND_NEAR_SELECT_BIAS; - } - - 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; - } - } - - 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_px_manhattan = 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_px_manhattan = len_manhattan_v2v2(data->mval_fl, screen_co_mid); - } - } -} - -BMEdge *EDBM_edge_find_nearest_ex(ViewContext *vc, - float *dist_px_manhattan_p, - float *r_dist_center_px_manhattan, - const bool use_select_bias, - bool use_cycle, - BMEdge **r_eed_zbuf, - Base **bases, - uint bases_len, - uint *r_base_index) -{ - uint base_index = 0; - - if (!XRAY_FLAG_ENABLED(vc->v3d)) { - uint dist_px_manhattan_test = (uint)ED_view3d_backbuf_sample_size_clamp(vc->region, - *dist_px_manhattan_p); - uint index; - BMEdge *eed; - - /* No afterqueue (yet), so we check it now, otherwise the bm_xxxofs indices are bad. */ - { - DRW_select_buffer_context_create(bases, bases_len, SCE_SELECT_EDGE); - - index = DRW_select_buffer_find_nearest_to_point( - vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px_manhattan_test); - - if (index) { - eed = (BMEdge *)edbm_select_id_bm_elem_get(bases, index, &base_index); - } - else { - eed = NULL; - } - } - - if (r_eed_zbuf) { - *r_eed_zbuf = eed; - } - - /* exception for faces (verts don't need this) */ - if (r_dist_center_px_manhattan && 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 | V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT); - - *r_dist_center_px_manhattan = data.dist; - } - /* end exception */ - - if (eed) { - if (dist_px_manhattan_test < *dist_px_manhattan_p) { - if (r_base_index) { - *r_base_index = base_index; - } - *dist_px_manhattan_p = dist_px_manhattan_test; - return eed; - } - } - return NULL; - } - - struct NearestEdgeUserData data = {{0}}; - const struct NearestEdgeUserData_Hit *hit = NULL; - /* interpolate along the edge before doing a clipping plane test */ - const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_DEFAULT & ~V3D_PROJ_TEST_CLIP_BB; - BMesh *prev_select_bm = NULL; - - static struct { - int index; - const BMEdge *elem; - const BMesh *bm; - } prev_select = {0}; - - data.vc = *vc; - data.mval_fl[0] = vc->mval[0]; - data.mval_fl[1] = vc->mval[1]; - data.use_select_bias = use_select_bias; - data.use_cycle = use_cycle; - - for (; base_index < bases_len; base_index++) { - Base *base_iter = bases[base_index]; - ED_view3d_viewcontext_init_object(vc, base_iter->object); - if (use_cycle && prev_select.bm == vc->em->bm && - prev_select.elem == BM_edge_at_index_find_or_table(vc->em->bm, prev_select.index)) { - data.cycle_index_prev = prev_select.index; - /* No need to compare in the rest of the loop. */ - use_cycle = false; - } - else { - data.cycle_index_prev = 0; - } - - data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias = - *dist_px_manhattan_p; - - ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); - mesh_foreachScreenEdge( - vc, find_nearest_edge__doClosest, &data, clip_flag | V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT); - - hit = (data.use_cycle && data.hit_cycle.edge) ? &data.hit_cycle : &data.hit; - - if (hit->dist < *dist_px_manhattan_p) { - if (r_base_index) { - *r_base_index = base_index; - } - *dist_px_manhattan_p = hit->dist; - prev_select_bm = vc->em->bm; - } - } - - if (hit == NULL) { - return NULL; - } - - if (r_dist_center_px_manhattan) { - *r_dist_center_px_manhattan = hit->dist_center_px_manhattan; - } - - prev_select.index = hit->index; - prev_select.elem = hit->edge; - prev_select.bm = prev_select_bm; - - return hit->edge; -} - -BMEdge *EDBM_edge_find_nearest(ViewContext *vc, float *dist_px_manhattan_p) -{ - BKE_view_layer_synced_ensure(vc->scene, vc->view_layer); - Base *base = BKE_view_layer_base_find(vc->view_layer, vc->obact); - return EDBM_edge_find_nearest_ex( - vc, dist_px_manhattan_p, NULL, false, false, NULL, &base, 1, NULL); -} - -/* find the distance to the face we already have */ -struct NearestFaceUserData_ZBuf { - float mval_fl[2]; - float dist_px_manhattan; - const BMFace *face_test; -}; - -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_px_manhattan) { - data->dist_px_manhattan = dist_test; - } - } -} - -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 NearestFaceUserData *data = userData; - float dist_test, dist_test_bias; - - 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 (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 (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_ex(ViewContext *vc, - float *dist_px_manhattan_p, - float *r_dist_center, - const bool use_zbuf_single_px, - const bool use_select_bias, - bool use_cycle, - BMFace **r_efa_zbuf, - Base **bases, - uint bases_len, - uint *r_base_index) -{ - uint base_index = 0; - - if (!XRAY_FLAG_ENABLED(vc->v3d)) { - float dist_test; - uint index; - BMFace *efa; - - { - uint dist_px_manhattan_test = 0; - if (*dist_px_manhattan_p != 0.0f && (use_zbuf_single_px == false)) { - dist_px_manhattan_test = (uint)ED_view3d_backbuf_sample_size_clamp(vc->region, - *dist_px_manhattan_p); - } - - DRW_select_buffer_context_create(bases, bases_len, SCE_SELECT_FACE); - - if (dist_px_manhattan_test == 0) { - index = DRW_select_buffer_sample_point(vc->depsgraph, vc->region, vc->v3d, vc->mval); - dist_test = 0.0f; - } - else { - index = DRW_select_buffer_find_nearest_to_point( - vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px_manhattan_test); - dist_test = dist_px_manhattan_test; - } - - if (index) { - efa = (BMFace *)edbm_select_id_bm_elem_get(bases, index, &base_index); - } - else { - efa = NULL; - } - } - - 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_px_manhattan = FLT_MAX; - data.face_test = efa; - - ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); - - mesh_foreachScreenFace( - vc, find_nearest_face_center__doZBuf, &data, V3D_PROJ_TEST_CLIP_DEFAULT); - - *r_dist_center = data.dist_px_manhattan; - } - /* end exception */ - - if (efa) { - if (dist_test < *dist_px_manhattan_p) { - if (r_base_index) { - *r_base_index = base_index; - } - *dist_px_manhattan_p = dist_test; - return efa; - } - } - return NULL; - } - - struct NearestFaceUserData data = {{0}}; - const struct NearestFaceUserData_Hit *hit = NULL; - const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_DEFAULT; - BMesh *prev_select_bm = NULL; - - static struct { - int index; - const BMFace *elem; - const BMesh *bm; - } prev_select = {0}; - - data.mval_fl[0] = vc->mval[0]; - data.mval_fl[1] = vc->mval[1]; - data.use_select_bias = use_select_bias; - data.use_cycle = use_cycle; - - for (; base_index < bases_len; base_index++) { - Base *base_iter = bases[base_index]; - ED_view3d_viewcontext_init_object(vc, base_iter->object); - if (use_cycle && prev_select.bm == vc->em->bm && - prev_select.elem == BM_face_at_index_find_or_table(vc->em->bm, prev_select.index)) { - data.cycle_index_prev = prev_select.index; - /* No need to compare in the rest of the loop. */ - use_cycle = false; - } - else { - data.cycle_index_prev = 0; - } - - data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias = - *dist_px_manhattan_p; - - ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); - mesh_foreachScreenFace(vc, findnearestface__doClosest, &data, clip_flag); - - hit = (data.use_cycle && data.hit_cycle.face) ? &data.hit_cycle : &data.hit; - - if (hit->dist < *dist_px_manhattan_p) { - if (r_base_index) { - *r_base_index = base_index; - } - *dist_px_manhattan_p = hit->dist; - prev_select_bm = vc->em->bm; - } - } - - if (hit == NULL) { - return NULL; - } - - if (r_dist_center) { - *r_dist_center = hit->dist; - } - - prev_select.index = hit->index; - prev_select.elem = hit->face; - prev_select.bm = prev_select_bm; - - return hit->face; -} - -BMFace *EDBM_face_find_nearest(ViewContext *vc, float *dist_px_manhattan_p) -{ - BKE_view_layer_synced_ensure(vc->scene, vc->view_layer); - Base *base = BKE_view_layer_base_find(vc->view_layer, vc->obact); - return EDBM_face_find_nearest_ex( - vc, dist_px_manhattan_p, NULL, false, false, false, NULL, &base, 1, 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 - * return 1 if found one - */ -static bool unified_findnearest(ViewContext *vc, - Base **bases, - const uint bases_len, - int *r_base_index, - BMVert **r_eve, - BMEdge **r_eed, - BMFace **r_efa) -{ - BMEditMesh *em = vc->em; - - const bool use_cycle = !WM_cursor_test_motion_and_update(vc->mval); - 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; - - struct { - struct { - BMVert *ele; - int base_index; - } v; - struct { - BMEdge *ele; - int base_index; - } e, e_zbuf; - struct { - BMFace *ele; - int base_index; - } f, f_zbuf; - } hit = {{NULL}}; - - /* no afterqueue (yet), so we check it now, otherwise the em_xxxofs indices are bad */ - - 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; - - uint base_index = 0; - BMFace *efa_zbuf = NULL; - BMFace *efa_test = EDBM_face_find_nearest_ex( - vc, &dist, dist_center_p, true, true, use_cycle, &efa_zbuf, bases, bases_len, &base_index); - - if (efa_test && dist_center_p) { - dist = min_ff(dist_margin, dist_center); - } - if (efa_test) { - hit.f.base_index = base_index; - hit.f.ele = efa_test; - } - if (efa_zbuf) { - hit.f_zbuf.base_index = base_index; - hit.f_zbuf.ele = efa_zbuf; - } - } - - 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; - - uint base_index = 0; - BMEdge *eed_zbuf = NULL; - BMEdge *eed_test = EDBM_edge_find_nearest_ex( - vc, &dist, dist_center_p, true, use_cycle, &eed_zbuf, bases, bases_len, &base_index); - - if (eed_test && dist_center_p) { - dist = min_ff(dist_margin, dist_center); - } - if (eed_test) { - hit.e.base_index = base_index; - hit.e.ele = eed_test; - } - if (eed_zbuf) { - hit.e_zbuf.base_index = base_index; - hit.e_zbuf.ele = eed_zbuf; - } - } - - if ((dist > 0.0f) && (em->selectmode & SCE_SELECT_VERTEX)) { - uint base_index = 0; - BMVert *eve_test = EDBM_vert_find_nearest_ex( - vc, &dist, true, use_cycle, bases, bases_len, &base_index); - - if (eve_test) { - hit.v.base_index = base_index; - hit.v.ele = eve_test; - } - } - - /* Return only one of 3 pointers, for front-buffer redraws. */ - if (hit.v.ele) { - hit.f.ele = NULL; - hit.e.ele = NULL; - } - else if (hit.e.ele) { - hit.f.ele = NULL; - } - - /* 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 ((hit.v.ele || hit.e.ele || hit.f.ele) == 0) { - if (hit.e_zbuf.ele) { - hit.e.base_index = hit.e_zbuf.base_index; - hit.e.ele = hit.e_zbuf.ele; - } - else if (hit.f_zbuf.ele) { - hit.f.base_index = hit.f_zbuf.base_index; - hit.f.ele = hit.f_zbuf.ele; - } - } - - /* Only one element type will be non-null. */ - BLI_assert(((hit.v.ele != NULL) + (hit.e.ele != NULL) + (hit.f.ele != NULL)) <= 1); - - if (hit.v.ele) { - *r_base_index = hit.v.base_index; - } - if (hit.e.ele) { - *r_base_index = hit.e.base_index; - } - if (hit.f.ele) { - *r_base_index = hit.f.base_index; - } - - *r_eve = hit.v.ele; - *r_eed = hit.e.ele; - *r_efa = hit.f.ele; - - return (hit.v.ele || hit.e.ele || hit.f.ele); -} - -#undef FAKE_SELECT_MODE_BEGIN -#undef FAKE_SELECT_MODE_END - -bool EDBM_unified_findnearest(ViewContext *vc, - Base **bases, - const uint bases_len, - int *r_base_index, - BMVert **r_eve, - BMEdge **r_eed, - BMFace **r_efa) -{ - return unified_findnearest(vc, bases, bases_len, r_base_index, r_eve, r_eed, r_efa); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Alternate Find Nearest Vert/Edge (optional boundary) - * - * \note This uses ray-cast method instead of back-buffer, - * currently used for poly-build. - * \{ */ - -bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, - Base **bases, - const uint bases_len, - bool use_boundary_vertices, - bool use_boundary_edges, - int *r_base_index_vert, - int *r_base_index_edge, - int *r_base_index_face, - struct BMVert **r_eve, - struct BMEdge **r_eed, - struct BMFace **r_efa) -{ - - const float mval_fl[2] = {UNPACK2(vc->mval)}; - float ray_origin[3], ray_direction[3]; - - struct { - uint base_index; - BMElem *ele; - } best = {0, NULL}; - /* Currently unused, keep since we may want to pick the best. */ - UNUSED_VARS(best); - - struct { - uint base_index; - BMElem *ele; - } best_vert = {0, NULL}; - - struct { - uint base_index; - BMElem *ele; - } best_edge = {0, NULL}; - - struct { - uint base_index; - BMElem *ele; - } best_face = {0, NULL}; - - if (ED_view3d_win_to_ray_clipped( - vc->depsgraph, vc->region, vc->v3d, mval_fl, ray_origin, ray_direction, true)) { - float dist_sq_best = FLT_MAX; - float dist_sq_best_vert = FLT_MAX; - float dist_sq_best_edge = FLT_MAX; - float dist_sq_best_face = FLT_MAX; - - const bool use_vert = (r_eve != NULL); - const bool use_edge = (r_eed != NULL); - const bool use_face = (r_efa != NULL); - - for (uint base_index = 0; base_index < bases_len; base_index++) { - Base *base_iter = bases[base_index]; - Object *obedit = base_iter->object; - - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMesh *bm = em->bm; - float imat3[3][3]; - - ED_view3d_viewcontext_init_object(vc, obedit); - copy_m3_m4(imat3, obedit->obmat); - invert_m3(imat3); - - const float(*coords)[3] = NULL; - { - Mesh *me_eval = (Mesh *)DEG_get_evaluated_id(vc->depsgraph, obedit->data); - if (me_eval->runtime.edit_data) { - coords = me_eval->runtime.edit_data->vertexCos; - } - } - - if (coords != NULL) { - BM_mesh_elem_index_ensure(bm, BM_VERT); - } - - if ((use_boundary_vertices || use_boundary_edges) && (use_vert || use_edge)) { - BMEdge *e; - BMIter eiter; - BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) { - if ((BM_elem_flag_test(e, BM_ELEM_HIDDEN) == false) && (BM_edge_is_boundary(e))) { - if (use_vert && use_boundary_vertices) { - for (uint j = 0; j < 2; j++) { - BMVert *v = *((&e->v1) + j); - float point[3]; - mul_v3_m4v3(point, obedit->obmat, coords ? coords[BM_elem_index_get(v)] : v->co); - const float dist_sq_test = dist_squared_to_ray_v3_normalized( - ray_origin, ray_direction, point); - if (dist_sq_test < dist_sq_best_vert) { - dist_sq_best_vert = dist_sq_test; - best_vert.base_index = base_index; - best_vert.ele = (BMElem *)v; - } - if (dist_sq_test < dist_sq_best) { - dist_sq_best = dist_sq_test; - best.base_index = base_index; - best.ele = (BMElem *)v; - } - } - } - - if (use_edge && use_boundary_edges) { - float point[3]; -#if 0 - const float dist_sq_test = dist_squared_ray_to_seg_v3( - ray_origin, ray_direction, e->v1->co, e->v2->co, point, &depth); -#else - if (coords) { - mid_v3_v3v3( - point, coords[BM_elem_index_get(e->v1)], coords[BM_elem_index_get(e->v2)]); - } - else { - mid_v3_v3v3(point, e->v1->co, e->v2->co); - } - mul_m4_v3(obedit->obmat, point); - const float dist_sq_test = dist_squared_to_ray_v3_normalized( - ray_origin, ray_direction, point); - if (dist_sq_test < dist_sq_best_edge) { - dist_sq_best_edge = dist_sq_test; - best_edge.base_index = base_index; - best_edge.ele = (BMElem *)e; - } - if (dist_sq_test < dist_sq_best) { - dist_sq_best = dist_sq_test; - best.base_index = base_index; - best.ele = (BMElem *)e; - } -#endif - } - } - } - } - /* Non boundary case. */ - if (use_vert && !use_boundary_vertices) { - BMVert *v; - BMIter viter; - BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(v, BM_ELEM_HIDDEN) == false) { - float point[3]; - mul_v3_m4v3(point, obedit->obmat, coords ? coords[BM_elem_index_get(v)] : v->co); - const float dist_sq_test = dist_squared_to_ray_v3_normalized( - ray_origin, ray_direction, point); - if (dist_sq_test < dist_sq_best_vert) { - dist_sq_best_vert = dist_sq_test; - best_vert.base_index = base_index; - best_vert.ele = (BMElem *)v; - } - if (dist_sq_test < dist_sq_best) { - dist_sq_best = dist_sq_test; - best.base_index = base_index; - best.ele = (BMElem *)v; - } - } - } - } - - if (use_edge && !use_boundary_edges) { - BMEdge *e; - BMIter eiter; - BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) == false) { - float point[3]; - if (coords) { - mid_v3_v3v3( - point, coords[BM_elem_index_get(e->v1)], coords[BM_elem_index_get(e->v2)]); - } - else { - mid_v3_v3v3(point, e->v1->co, e->v2->co); - } - mul_m4_v3(obedit->obmat, point); - const float dist_sq_test = dist_squared_to_ray_v3_normalized( - ray_origin, ray_direction, point); - if (dist_sq_test < dist_sq_best_edge) { - dist_sq_best_edge = dist_sq_test; - best_edge.base_index = base_index; - best_edge.ele = (BMElem *)e; - } - if (dist_sq_test < dist_sq_best) { - dist_sq_best = dist_sq_test; - best.base_index = base_index; - best.ele = (BMElem *)e; - } - } - } - } - - if (use_face) { - BMFace *f; - BMIter fiter; - BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(f, BM_ELEM_HIDDEN) == false) { - float point[3]; - if (coords) { - BM_face_calc_center_median_vcos(bm, f, point, coords); - } - else { - BM_face_calc_center_median(f, point); - } - mul_m4_v3(obedit->obmat, point); - const float dist_sq_test = dist_squared_to_ray_v3_normalized( - ray_origin, ray_direction, point); - if (dist_sq_test < dist_sq_best_face) { - dist_sq_best_face = dist_sq_test; - best_face.base_index = base_index; - best_face.ele = (BMElem *)f; - } - if (dist_sq_test < dist_sq_best) { - dist_sq_best = dist_sq_test; - best.base_index = base_index; - best.ele = (BMElem *)f; - } - } - } - } - } - } - - *r_base_index_vert = best_vert.base_index; - *r_base_index_edge = best_edge.base_index; - *r_base_index_face = best_face.base_index; - - if (r_eve) { - *r_eve = NULL; - } - if (r_eed) { - *r_eed = NULL; - } - if (r_efa) { - *r_efa = NULL; - } - - if (best_vert.ele) { - *r_eve = (BMVert *)best_vert.ele; - } - if (best_edge.ele) { - *r_eed = (BMEdge *)best_edge.ele; - } - if (best_face.ele) { - *r_efa = (BMFace *)best_face.ele; - } - - return (best_vert.ele != NULL || best_edge.ele != NULL || best_face.ele != NULL); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Similar Region Operator - * \{ */ - -static int edbm_select_similar_region_exec(bContext *C, wmOperator *op) -{ - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMesh *bm = em->bm; - bool changed = false; - - /* group vars */ - int *groups_array; - int(*group_index)[2]; - int group_tot; - int i; - - if (bm->totfacesel < 2) { - BKE_report(op->reports, RPT_ERROR, "No face regions selected"); - return OPERATOR_CANCELLED; - } - - groups_array = MEM_mallocN(sizeof(*groups_array) * bm->totfacesel, __func__); - group_tot = BM_mesh_calc_face_groups( - bm, groups_array, &group_index, NULL, NULL, NULL, BM_ELEM_SELECT, BM_VERT); - - BM_mesh_elem_table_ensure(bm, BM_FACE); - - for (i = 0; i < group_tot; i++) { - ListBase faces_regions; - int tot; - - const int fg_sta = group_index[i][0]; - const int fg_len = group_index[i][1]; - int j; - BMFace **fg = MEM_mallocN(sizeof(*fg) * fg_len, __func__); - - for (j = 0; j < fg_len; j++) { - fg[j] = BM_face_at_index(bm, groups_array[fg_sta + j]); - } - - tot = BM_mesh_region_match(bm, fg, fg_len, &faces_regions); - - MEM_freeN(fg); - - if (tot) { - LinkData *link; - while ((link = BLI_pophead(&faces_regions))) { - BMFace *f, **faces = link->data; - while ((f = *(faces++))) { - BM_face_select_set(bm, f, true); - } - MEM_freeN(link->data); - MEM_freeN(link); - - changed = true; - } - } - } - - MEM_freeN(groups_array); - MEM_freeN(group_index); - - if (changed) { - DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); - } - else { - BKE_report(op->reports, RPT_WARNING, "No matching face regions found"); - } - - return OPERATOR_FINISHED; -} - -void MESH_OT_select_similar_region(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Similar Regions"; - ot->idname = "MESH_OT_select_similar_region"; - ot->description = "Select similar face regions to the current selection"; - - /* api callbacks */ - ot->exec = edbm_select_similar_region_exec; - ot->poll = ED_operator_editmesh; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Mode Vert/Edge/Face Operator - * \{ */ - -static int edbm_select_mode_exec(bContext *C, wmOperator *op) -{ - const int type = RNA_enum_get(op->ptr, "type"); - const int action = RNA_enum_get(op->ptr, "action"); - const bool use_extend = RNA_boolean_get(op->ptr, "use_extend"); - const bool use_expand = RNA_boolean_get(op->ptr, "use_expand"); - - if (EDBM_selectmode_toggle_multi(C, type, action, use_extend, use_expand)) { - return OPERATOR_FINISHED; - } - return OPERATOR_CANCELLED; -} - -static int edbm_select_mode_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - /* Bypass when in UV non sync-select mode, fall through to keymap that edits. */ - if (CTX_wm_space_image(C)) { - ToolSettings *ts = CTX_data_tool_settings(C); - if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) { - return OPERATOR_PASS_THROUGH; - } - /* Bypass when no action is needed. */ - if (!RNA_struct_property_is_set(op->ptr, "type")) { - return OPERATOR_CANCELLED; - } - } - - /* detecting these options based on shift/ctrl here is weak, but it's done - * to make this work when clicking buttons or menus */ - if (!RNA_struct_property_is_set(op->ptr, "use_extend")) { - RNA_boolean_set(op->ptr, "use_extend", event->modifier & KM_SHIFT); - } - if (!RNA_struct_property_is_set(op->ptr, "use_expand")) { - RNA_boolean_set(op->ptr, "use_expand", event->modifier & KM_CTRL); - } - - return edbm_select_mode_exec(C, op); -} - -static char *edbm_select_mode_get_description(struct bContext *UNUSED(C), - struct wmOperatorType *UNUSED(op), - struct PointerRNA *values) -{ - const int type = RNA_enum_get(values, "type"); - - /* Because the special behavior for shift and ctrl click depend on user input, they may be - * incorrect if the operator is used from a script or from a special button. So only return the - * specialized descriptions if only the "type" is set, which conveys that the operator is meant - * to be used with the logic in the `invoke` method. */ - if (RNA_struct_property_is_set(values, "type") && - !RNA_struct_property_is_set(values, "use_extend") && - !RNA_struct_property_is_set(values, "use_expand") && - !RNA_struct_property_is_set(values, "action")) { - switch (type) { - case SCE_SELECT_VERTEX: - return BLI_strdup(TIP_( - "Vertex select - Shift-Click for multiple modes, Ctrl-Click contracts selection")); - case SCE_SELECT_EDGE: - return BLI_strdup( - TIP_("Edge select - Shift-Click for multiple modes, " - "Ctrl-Click expands/contracts selection depending on the current mode")); - case SCE_SELECT_FACE: - return BLI_strdup( - TIP_("Face select - Shift-Click for multiple modes, Ctrl-Click expands selection")); - } - } - - return NULL; -} - -void MESH_OT_select_mode(wmOperatorType *ot) -{ - PropertyRNA *prop; - - static const EnumPropertyItem actions_items[] = { - {0, "DISABLE", 0, "Disable", "Disable selected markers"}, - {1, "ENABLE", 0, "Enable", "Enable selected markers"}, - {2, "TOGGLE", 0, "Toggle", "Toggle disabled flag for selected markers"}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Select Mode"; - ot->idname = "MESH_OT_select_mode"; - ot->description = "Change selection mode"; - - /* api callbacks */ - ot->invoke = edbm_select_mode_invoke; - ot->exec = edbm_select_mode_exec; - ot->poll = ED_operator_editmesh; - ot->get_description = edbm_select_mode_get_description; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - /* Hide all, not to show redo panel. */ - prop = RNA_def_boolean(ot->srna, "use_extend", false, "Extend", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "use_expand", false, "Expand", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - ot->prop = prop = RNA_def_enum(ot->srna, "type", rna_enum_mesh_select_mode_items, 0, "Type", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - - prop = RNA_def_enum( - ot->srna, "action", actions_items, 2, "Action", "Selection action to execute"); - RNA_def_property_flag(prop, PROP_HIDDEN); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Loop (Non Modal) Operator - * \{ */ - -static void walker_select_count(BMEditMesh *em, - int walkercode, - void *start, - int r_count_by_select[2]) -{ - BMesh *bm = em->bm; - BMElem *ele; - BMWalker walker; - - r_count_by_select[0] = r_count_by_select[1] = 0; - - BMW_init(&walker, - bm, - walkercode, - BMW_MASK_NOP, - BMW_MASK_NOP, - BMW_MASK_NOP, - BMW_FLAG_TEST_HIDDEN, - BMW_NIL_LAY); - - for (ele = BMW_begin(&walker, start); ele; ele = BMW_step(&walker)) { - r_count_by_select[BM_elem_flag_test(ele, BM_ELEM_SELECT) ? 1 : 0] += 1; - - /* Early exit when mixed (could be optional if needed. */ - if (r_count_by_select[0] && r_count_by_select[1]) { - r_count_by_select[0] = r_count_by_select[1] = -1; - break; - } - } - - BMW_end(&walker); -} - -static void walker_select(BMEditMesh *em, int walkercode, void *start, const bool select) -{ - BMesh *bm = em->bm; - BMElem *ele; - BMWalker walker; - - BMW_init(&walker, - bm, - walkercode, - BMW_MASK_NOP, - BMW_MASK_NOP, - BMW_MASK_NOP, - BMW_FLAG_TEST_HIDDEN, - BMW_NIL_LAY); - - for (ele = BMW_begin(&walker, start); ele; ele = BMW_step(&walker)) { - if (!select) { - BM_select_history_remove(bm, ele); - } - BM_elem_select_set(bm, ele, select); - } - BMW_end(&walker); -} - -static int edbm_loop_multiselect_exec(bContext *C, wmOperator *op) -{ - const bool is_ring = RNA_boolean_get(op->ptr, "ring"); - const Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - scene, view_layer, CTX_wm_view3d(C), &objects_len); - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - - if (em->bm->totedgesel == 0) { - continue; - } - - BMEdge *eed; - BMEdge **edarray; - int edindex; - BMIter iter; - int totedgesel = 0; - - BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) { - totedgesel++; - } - } - - edarray = MEM_mallocN(sizeof(BMEdge *) * totedgesel, "edge array"); - edindex = 0; - - BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) { - edarray[edindex] = eed; - edindex++; - } - } - - if (is_ring) { - for (edindex = 0; edindex < totedgesel; edindex += 1) { - eed = edarray[edindex]; - walker_select(em, BMW_EDGERING, eed, true); - } - EDBM_selectmode_flush(em); - } - else { - for (edindex = 0; edindex < totedgesel; edindex += 1) { - eed = edarray[edindex]; - bool non_manifold = BM_edge_face_count_is_over(eed, 2); - if (non_manifold) { - walker_select(em, BMW_EDGELOOP_NONMANIFOLD, eed, true); - } - else { - walker_select(em, BMW_EDGELOOP, eed, true); - } - } - EDBM_selectmode_flush(em); - } - MEM_freeN(edarray); - // if (EM_texFaceCheck()) - - DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); - } - MEM_freeN(objects); - - return OPERATOR_FINISHED; -} - -void MESH_OT_loop_multi_select(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Multi Select Loops"; - ot->idname = "MESH_OT_loop_multi_select"; - ot->description = "Select a loop of connected edges by connection type"; - - /* api callbacks */ - ot->exec = edbm_loop_multiselect_exec; - ot->poll = ED_operator_editmesh; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean(ot->srna, "ring", 0, "Ring", ""); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Loop (Cursor Pick) Operator - * \{ */ - -static void mouse_mesh_loop_face(BMEditMesh *em, BMEdge *eed, bool select, bool select_clear) -{ - if (select_clear) { - EDBM_flag_disable_all(em, BM_ELEM_SELECT); - } - - walker_select(em, BMW_FACELOOP, eed, select); -} - -static void mouse_mesh_loop_edge_ring(BMEditMesh *em, BMEdge *eed, bool select, bool select_clear) -{ - if (select_clear) { - EDBM_flag_disable_all(em, BM_ELEM_SELECT); - } - - walker_select(em, BMW_EDGERING, eed, select); -} - -static void mouse_mesh_loop_edge( - BMEditMesh *em, BMEdge *eed, bool select, bool select_clear, bool select_cycle) -{ - bool edge_boundary = false; - bool non_manifold = BM_edge_face_count_is_over(eed, 2); - - /* Cycle between BMW_EDGELOOP / BMW_EDGEBOUNDARY. */ - if (select_cycle && BM_edge_is_boundary(eed)) { - int count_by_select[2]; - - /* If the loops selected toggle the boundaries. */ - walker_select_count(em, BMW_EDGELOOP, eed, count_by_select); - if (count_by_select[!select] == 0) { - edge_boundary = true; - - /* If the boundaries selected, toggle back to the loop. */ - walker_select_count(em, BMW_EDGEBOUNDARY, eed, count_by_select); - if (count_by_select[!select] == 0) { - edge_boundary = false; - } - } - } - - if (select_clear) { - EDBM_flag_disable_all(em, BM_ELEM_SELECT); - } - - if (edge_boundary) { - walker_select(em, BMW_EDGEBOUNDARY, eed, select); - } - else if (non_manifold) { - walker_select(em, BMW_EDGELOOP_NONMANIFOLD, eed, select); - } - else { - walker_select(em, BMW_EDGELOOP, eed, select); - } -} - -static bool mouse_mesh_loop( - bContext *C, const int mval[2], bool extend, bool deselect, bool toggle, bool ring) -{ - Base *basact = NULL; - BMVert *eve = NULL; - BMEdge *eed = NULL; - BMFace *efa = NULL; - - ViewContext vc; - BMEditMesh *em; - bool select = true; - bool select_clear = false; - bool select_cycle = true; - float mvalf[2]; - - em_setup_viewcontext(C, &vc); - mvalf[0] = (float)(vc.mval[0] = mval[0]); - mvalf[1] = (float)(vc.mval[1] = mval[1]); - - BMEditMesh *em_original = vc.em; - const short selectmode = em_original->selectmode; - em_original->selectmode = SCE_SELECT_EDGE; - - uint bases_len; - Base **bases = BKE_view_layer_array_from_bases_in_edit_mode( - vc.scene, vc.view_layer, vc.v3d, &bases_len); - - { - int base_index = -1; - if (EDBM_unified_findnearest(&vc, bases, bases_len, &base_index, &eve, &eed, &efa)) { - basact = bases[base_index]; - ED_view3d_viewcontext_init_object(&vc, basact->object); - em = vc.em; - } - else { - em = NULL; - } - } - - em_original->selectmode = selectmode; - - if (em == NULL || eed == NULL) { - MEM_freeN(bases); - return false; - } - - if (extend == false && deselect == false && toggle == false) { - select_clear = true; - } - - if (extend) { - select = true; - } - else if (deselect) { - select = false; - } - else if (select_clear || (BM_elem_flag_test(eed, BM_ELEM_SELECT) == 0)) { - select = true; - } - else if (toggle) { - select = false; - select_cycle = false; - } - - if (select_clear) { - for (uint base_index = 0; base_index < bases_len; base_index++) { - Base *base_iter = bases[base_index]; - Object *ob_iter = base_iter->object; - BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter); - - if (em_iter->bm->totvertsel == 0) { - continue; - } - - if (em_iter == em) { - continue; - } - - EDBM_flag_disable_all(em_iter, BM_ELEM_SELECT); - DEG_id_tag_update(ob_iter->data, ID_RECALC_SELECT); - } - } - - if (em->selectmode & SCE_SELECT_FACE) { - mouse_mesh_loop_face(em, eed, select, select_clear); - } - else { - if (ring) { - mouse_mesh_loop_edge_ring(em, eed, select, select_clear); - } - else { - mouse_mesh_loop_edge(em, eed, select, select_clear, select_cycle); - } - } - - EDBM_selectmode_flush(em); - - /* sets as active, useful for other tools */ - if (select) { - if (em->selectmode & SCE_SELECT_VERTEX) { - /* Find nearest vert from mouse - * (initialize to large values in case only one vertex can be projected) */ - float v1_co[2], v2_co[2]; - float length_1 = FLT_MAX; - float length_2 = FLT_MAX; - - /* We can't be sure this has already been set... */ - ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d); - - if (ED_view3d_project_float_object(vc.region, eed->v1->co, v1_co, V3D_PROJ_TEST_CLIP_NEAR) == - V3D_PROJ_RET_OK) { - length_1 = len_squared_v2v2(mvalf, v1_co); - } - - if (ED_view3d_project_float_object(vc.region, eed->v2->co, v2_co, V3D_PROJ_TEST_CLIP_NEAR) == - V3D_PROJ_RET_OK) { - length_2 = len_squared_v2v2(mvalf, v2_co); - } -#if 0 - printf("mouse to v1: %f\nmouse to v2: %f\n", - len_squared_v2v2(mvalf, v1_co), - len_squared_v2v2(mvalf, v2_co)); -#endif - BM_select_history_store(em->bm, (length_1 < length_2) ? eed->v1 : eed->v2); - } - else if (em->selectmode & SCE_SELECT_EDGE) { - BM_select_history_store(em->bm, eed); - } - else if (em->selectmode & SCE_SELECT_FACE) { - /* Select the face of eed which is the nearest of mouse. */ - BMFace *f; - BMIter iterf; - float best_dist = FLT_MAX; - efa = NULL; - - /* We can't be sure this has already been set... */ - ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d); - - BM_ITER_ELEM (f, &iterf, eed, BM_FACES_OF_EDGE) { - if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { - float cent[3]; - float co[2], tdist; - - BM_face_calc_center_median(f, cent); - if (ED_view3d_project_float_object(vc.region, cent, co, V3D_PROJ_TEST_CLIP_NEAR) == - V3D_PROJ_RET_OK) { - tdist = len_squared_v2v2(mvalf, co); - if (tdist < best_dist) { - // printf("Best face: %p (%f)\n", f, tdist); - best_dist = tdist; - efa = f; - } - } - } - } - if (efa) { - BM_mesh_active_face_set(em->bm, efa); - BM_select_history_store(em->bm, efa); - } - } - } - - MEM_freeN(bases); - - DEG_id_tag_update(vc.obedit->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); - - return true; -} - -static int edbm_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - - view3d_operator_needs_opengl(C); - - if (mouse_mesh_loop(C, - event->mval, - RNA_boolean_get(op->ptr, "extend"), - RNA_boolean_get(op->ptr, "deselect"), - RNA_boolean_get(op->ptr, "toggle"), - RNA_boolean_get(op->ptr, "ring"))) { - return OPERATOR_FINISHED; - } - return OPERATOR_CANCELLED; -} - -void MESH_OT_loop_select(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Loop Select"; - ot->idname = "MESH_OT_loop_select"; - ot->description = "Select a loop of connected edges"; - - /* api callbacks */ - ot->invoke = edbm_select_loop_invoke; - ot->poll = ED_operator_editmesh_region_view3d; - - /* flags */ - ot->flag = OPTYPE_UNDO; - - /* properties */ - PropertyRNA *prop; - - prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "Extend the selection"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Remove from the selection"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle Select", "Toggle the selection"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "ring", 0, "Select Ring", "Select ring"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); -} - -void MESH_OT_edgering_select(wmOperatorType *ot) -{ - /* description */ - ot->name = "Edge Ring Select"; - ot->idname = "MESH_OT_edgering_select"; - ot->description = "Select an edge ring"; - - /* callbacks */ - ot->invoke = edbm_select_loop_invoke; - ot->poll = ED_operator_editmesh_region_view3d; - - /* flags */ - ot->flag = OPTYPE_UNDO; - - /* Properties. */ - PropertyRNA *prop; - prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Remove from the selection"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle Select", "Toggle the selection"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "ring", 1, "Select Ring", "Select ring"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name (De)Select All Operator - * \{ */ - -static int edbm_select_all_exec(bContext *C, wmOperator *op) -{ - const Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - int action = RNA_enum_get(op->ptr, "action"); - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - scene, view_layer, CTX_wm_view3d(C), &objects_len); - - if (action == SEL_TOGGLE) { - action = SEL_SELECT; - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - if (em->bm->totvertsel || em->bm->totedgesel || em->bm->totfacesel) { - action = SEL_DESELECT; - break; - } - } - } - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - switch (action) { - case SEL_SELECT: - EDBM_flag_enable_all(em, BM_ELEM_SELECT); - break; - case SEL_DESELECT: - EDBM_flag_disable_all(em, BM_ELEM_SELECT); - break; - case SEL_INVERT: - EDBM_select_swap(em); - EDBM_selectmode_flush(em); - break; - } - DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); - } - - MEM_freeN(objects); - - return OPERATOR_FINISHED; -} - -void MESH_OT_select_all(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "(De)select All"; - ot->idname = "MESH_OT_select_all"; - ot->description = "(De)select all vertices, edges or faces"; - - /* api callbacks */ - ot->exec = edbm_select_all_exec; - ot->poll = ED_operator_editmesh; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - WM_operator_properties_select_all(ot); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Interior Faces Operator - * \{ */ - -static int edbm_faces_select_interior_exec(bContext *C, wmOperator *UNUSED(op)) -{ - const Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - scene, view_layer, CTX_wm_view3d(C), &objects_len); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - - if (!EDBM_select_interior_faces(em)) { - continue; - } - - DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); - } - MEM_freeN(objects); - - return OPERATOR_FINISHED; -} - -void MESH_OT_select_interior_faces(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Interior Faces"; - ot->idname = "MESH_OT_select_interior_faces"; - ot->description = "Select faces where all edges have more than 2 face users"; - - /* api callbacks */ - ot->exec = edbm_faces_select_interior_exec; - ot->poll = ED_operator_editmesh; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Picking API - * - * Here actual select happens, - * Gets called via generic mouse select operator. - * \{ */ - -bool EDBM_select_pick(bContext *C, const int mval[2], const struct SelectPick_Params *params) -{ - ViewContext vc; - - int base_index_active = -1; - BMVert *eve = NULL; - BMEdge *eed = NULL; - BMFace *efa = NULL; - - /* setup view context for argument to callbacks */ - em_setup_viewcontext(C, &vc); - vc.mval[0] = mval[0]; - vc.mval[1] = mval[1]; - - uint bases_len = 0; - Base **bases = BKE_view_layer_array_from_bases_in_edit_mode( - vc.scene, vc.view_layer, vc.v3d, &bases_len); - - bool changed = false; - bool found = unified_findnearest(&vc, bases, bases_len, &base_index_active, &eve, &eed, &efa); - - if (params->sel_op == SEL_OP_SET) { - BMElem *ele = efa ? (BMElem *)efa : (eed ? (BMElem *)eed : (BMElem *)eve); - if ((found && params->select_passthrough) && BM_elem_flag_test(ele, BM_ELEM_SELECT)) { - found = false; - } - else if (found || params->deselect_all) { - /* Deselect everything. */ - for (uint base_index = 0; base_index < bases_len; base_index++) { - Base *base_iter = bases[base_index]; - Object *ob_iter = base_iter->object; - EDBM_flag_disable_all(BKE_editmesh_from_object(ob_iter), BM_ELEM_SELECT); - DEG_id_tag_update(ob_iter->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data); - } - changed = true; - } - } - - if (found) { - Base *basact = bases[base_index_active]; - ED_view3d_viewcontext_init_object(&vc, basact->object); - - if (efa) { - switch (params->sel_op) { - case SEL_OP_ADD: { - BM_mesh_active_face_set(vc.em->bm, efa); - - /* Work-around: deselect first, so we can guarantee it will - * be active even if it was already selected. */ - BM_select_history_remove(vc.em->bm, efa); - BM_face_select_set(vc.em->bm, efa, false); - BM_select_history_store(vc.em->bm, efa); - BM_face_select_set(vc.em->bm, efa, true); - break; - } - case SEL_OP_SUB: { - BM_select_history_remove(vc.em->bm, efa); - BM_face_select_set(vc.em->bm, efa, false); - break; - } - case SEL_OP_XOR: { - BM_mesh_active_face_set(vc.em->bm, efa); - if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - BM_select_history_store(vc.em->bm, efa); - BM_face_select_set(vc.em->bm, efa, true); - } - else { - BM_select_history_remove(vc.em->bm, efa); - BM_face_select_set(vc.em->bm, efa, false); - } - break; - } - case SEL_OP_SET: { - BM_mesh_active_face_set(vc.em->bm, efa); - if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - BM_select_history_store(vc.em->bm, efa); - BM_face_select_set(vc.em->bm, efa, true); - } - break; - } - case SEL_OP_AND: { - BLI_assert_unreachable(); /* Doesn't make sense for picking. */ - break; - } - } - } - else if (eed) { - - switch (params->sel_op) { - case SEL_OP_ADD: { - /* Work-around: deselect first, so we can guarantee it will - * be active even if it was already selected. */ - BM_select_history_remove(vc.em->bm, eed); - BM_edge_select_set(vc.em->bm, eed, false); - BM_select_history_store(vc.em->bm, eed); - BM_edge_select_set(vc.em->bm, eed, true); - break; - } - case SEL_OP_SUB: { - BM_select_history_remove(vc.em->bm, eed); - BM_edge_select_set(vc.em->bm, eed, false); - break; - } - case SEL_OP_XOR: { - if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) { - BM_select_history_store(vc.em->bm, eed); - BM_edge_select_set(vc.em->bm, eed, true); - } - else { - BM_select_history_remove(vc.em->bm, eed); - BM_edge_select_set(vc.em->bm, eed, false); - } - break; - } - case SEL_OP_SET: { - if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) { - BM_select_history_store(vc.em->bm, eed); - BM_edge_select_set(vc.em->bm, eed, true); - } - break; - } - case SEL_OP_AND: { - BLI_assert_unreachable(); /* Doesn't make sense for picking. */ - break; - } - } - } - else if (eve) { - switch (params->sel_op) { - case SEL_OP_ADD: { - /* Work-around: deselect first, so we can guarantee it will - * be active even if it was already selected. */ - BM_select_history_remove(vc.em->bm, eve); - BM_vert_select_set(vc.em->bm, eve, false); - BM_select_history_store(vc.em->bm, eve); - BM_vert_select_set(vc.em->bm, eve, true); - break; - } - case SEL_OP_SUB: { - BM_select_history_remove(vc.em->bm, eve); - BM_vert_select_set(vc.em->bm, eve, false); - break; - } - case SEL_OP_XOR: { - if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - BM_select_history_store(vc.em->bm, eve); - BM_vert_select_set(vc.em->bm, eve, true); - } - else { - BM_select_history_remove(vc.em->bm, eve); - BM_vert_select_set(vc.em->bm, eve, false); - } - break; - } - case SEL_OP_SET: { - if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - BM_select_history_store(vc.em->bm, eve); - BM_vert_select_set(vc.em->bm, eve, true); - } - break; - } - case SEL_OP_AND: { - BLI_assert_unreachable(); /* Doesn't make sense for picking. */ - break; - } - } - } - - EDBM_selectmode_flush(vc.em); - - if (efa) { - /* Change active material on object. */ - if (efa->mat_nr != vc.obedit->actcol - 1) { - vc.obedit->actcol = efa->mat_nr + 1; - vc.em->mat_nr = efa->mat_nr; - WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, NULL); - } - - /* Change active face-map on object. */ - if (!BLI_listbase_is_empty(&vc.obedit->fmaps)) { - const int cd_fmap_offset = CustomData_get_offset(&vc.em->bm->pdata, CD_FACEMAP); - if (cd_fmap_offset != -1) { - int map = *((int *)BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset)); - if ((map < -1) || (map > BLI_listbase_count_at_most(&vc.obedit->fmaps, map))) { - map = -1; - } - map += 1; - if (map != vc.obedit->actfmap) { - /* We may want to add notifiers later, - * currently select update handles redraw. */ - vc.obedit->actfmap = map; - } - } - } - } - - /* Changing active object is handy since it allows us to - * switch UV layers, vgroups for eg. */ - BKE_view_layer_synced_ensure(vc.scene, vc.view_layer); - if (BKE_view_layer_active_base_get(vc.view_layer) != basact) { - ED_object_base_activate(C, basact); - } - - DEG_id_tag_update(vc.obedit->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); - - changed = true; - } - - MEM_freeN(bases); - - return changed; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Mode Utilities - * \{ */ - -static void edbm_strip_selections(BMEditMesh *em) -{ - BMEditSelection *ese, *nextese; - - if (!(em->selectmode & SCE_SELECT_VERTEX)) { - ese = em->bm->selected.first; - while (ese) { - nextese = ese->next; - if (ese->htype == BM_VERT) { - BLI_freelinkN(&(em->bm->selected), ese); - } - ese = nextese; - } - } - if (!(em->selectmode & SCE_SELECT_EDGE)) { - ese = em->bm->selected.first; - while (ese) { - nextese = ese->next; - if (ese->htype == BM_EDGE) { - BLI_freelinkN(&(em->bm->selected), ese); - } - ese = nextese; - } - } - if (!(em->selectmode & SCE_SELECT_FACE)) { - ese = em->bm->selected.first; - while (ese) { - nextese = ese->next; - if (ese->htype == BM_FACE) { - BLI_freelinkN(&(em->bm->selected), ese); - } - ese = nextese; - } - } -} - -void EDBM_selectmode_set(BMEditMesh *em) -{ - BMVert *eve; - BMEdge *eed; - BMFace *efa; - BMIter iter; - - em->bm->selectmode = em->selectmode; - - /* strip BMEditSelections from em->selected that are not relevant to new mode */ - edbm_strip_selections(em); - - if (em->bm->totvertsel == 0 && em->bm->totedgesel == 0 && em->bm->totfacesel == 0) { - return; - } - - if (em->selectmode & SCE_SELECT_VERTEX) { - if (em->bm->totvertsel) { - EDBM_select_flush(em); - } - } - else if (em->selectmode & SCE_SELECT_EDGE) { - /* deselect vertices, and select again based on edge select */ - BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { - BM_vert_select_set(em->bm, eve, false); - } - - if (em->bm->totedgesel) { - BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) { - BM_edge_select_set(em->bm, eed, true); - } - } - - /* selects faces based on edge status */ - EDBM_selectmode_flush(em); - } - } - else if (em->selectmode & SCE_SELECT_FACE) { - /* Deselect edges, and select again based on face select. */ - BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { - BM_edge_select_set(em->bm, eed, false); - } - - if (em->bm->totfacesel) { - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - BM_face_select_set(em->bm, efa, true); - } - } - } - } -} - -void EDBM_selectmode_convert(BMEditMesh *em, - const short selectmode_old, - const short selectmode_new) -{ - BMesh *bm = em->bm; - - BMVert *eve; - BMEdge *eed; - BMFace *efa; - BMIter iter; - - /* first tag-to-select, then select --- this avoids a feedback loop */ - - /* Have to find out what the selection-mode was previously. */ - if (selectmode_old == SCE_SELECT_VERTEX) { - if (bm->totvertsel == 0) { - /* pass */ - } - else if (selectmode_new == SCE_SELECT_EDGE) { - /* flush up (vert -> edge) */ - - /* select all edges associated with every selected vert */ - BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { - BM_elem_flag_set(eed, BM_ELEM_TAG, BM_edge_is_any_vert_flag_test(eed, BM_ELEM_SELECT)); - } - - BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(eed, BM_ELEM_TAG)) { - BM_edge_select_set(bm, eed, true); - } - } - } - else if (selectmode_new == SCE_SELECT_FACE) { - /* flush up (vert -> face) */ - - /* select all faces associated with every selected vert */ - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - BM_elem_flag_set(efa, BM_ELEM_TAG, BM_face_is_any_vert_flag_test(efa, BM_ELEM_SELECT)); - } - - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(efa, BM_ELEM_TAG)) { - BM_face_select_set(bm, efa, true); - } - } - } - } - else if (selectmode_old == SCE_SELECT_EDGE) { - if (bm->totedgesel == 0) { - /* pass */ - } - else if (selectmode_new == SCE_SELECT_FACE) { - /* flush up (edge -> face) */ - - /* select all faces associated with every selected edge */ - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - BM_elem_flag_set(efa, BM_ELEM_TAG, BM_face_is_any_edge_flag_test(efa, BM_ELEM_SELECT)); - } - - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(efa, BM_ELEM_TAG)) { - BM_face_select_set(bm, efa, true); - } - } - } - else if (selectmode_new == SCE_SELECT_VERTEX) { - /* flush down (edge -> vert) */ - - BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { - if (!BM_vert_is_all_edge_flag_test(eve, BM_ELEM_SELECT, true)) { - BM_vert_select_set(bm, eve, false); - } - } - /* deselect edges without both verts selected */ - BM_mesh_deselect_flush(bm); - } - } - else if (selectmode_old == SCE_SELECT_FACE) { - if (bm->totfacesel == 0) { - /* pass */ - } - else if (selectmode_new == SCE_SELECT_EDGE) { - /* flush down (face -> edge) */ - - BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { - if (!BM_edge_is_all_face_flag_test(eed, BM_ELEM_SELECT, true)) { - BM_edge_select_set(bm, eed, false); - } - } - /* Deselect faces without edges selected. */ - BM_mesh_deselect_flush(bm); - } - else if (selectmode_new == SCE_SELECT_VERTEX) { - /* flush down (face -> vert) */ - - BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { - if (!BM_vert_is_all_face_flag_test(eve, BM_ELEM_SELECT, true)) { - BM_vert_select_set(bm, eve, false); - } - } - /* deselect faces without verts selected */ - BM_mesh_deselect_flush(bm); - } - } -} - -bool EDBM_selectmode_toggle_multi(bContext *C, - const short selectmode_new, - const int action, - const bool use_extend, - const bool use_expand) -{ - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - ToolSettings *ts = CTX_data_tool_settings(C); - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = NULL; - bool ret = false; - - if (obedit && obedit->type == OB_MESH) { - em = BKE_editmesh_from_object(obedit); - } - - if (em == NULL) { - return ret; - } - - bool only_update = false; - switch (action) { - case -1: - /* already set */ - break; - case 0: /* disable */ - /* check we have something to do */ - if ((em->selectmode & selectmode_new) == 0) { - only_update = true; - break; - } - em->selectmode &= ~selectmode_new; - break; - case 1: /* enable */ - /* check we have something to do */ - if ((em->selectmode & selectmode_new) != 0) { - only_update = true; - break; - } - em->selectmode |= selectmode_new; - break; - case 2: /* toggle */ - /* can't disable this flag if its the only one set */ - if (em->selectmode == selectmode_new) { - only_update = true; - break; - } - em->selectmode ^= selectmode_new; - break; - default: - BLI_assert(0); - break; - } - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - scene, view_layer, CTX_wm_view3d(C), &objects_len); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *ob_iter = objects[ob_index]; - BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter); - if (em_iter != em) { - em_iter->selectmode = em->selectmode; - } - } - - if (only_update) { - MEM_freeN(objects); - return false; - } - - if (use_extend == 0 || em->selectmode == 0) { - if (use_expand) { - const short selmode_max = highest_order_bit_s(ts->selectmode); - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *ob_iter = objects[ob_index]; - BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter); - EDBM_selectmode_convert(em_iter, selmode_max, selectmode_new); - } - } - } - - switch (selectmode_new) { - case SCE_SELECT_VERTEX: - if (use_extend == 0 || em->selectmode == 0) { - em->selectmode = SCE_SELECT_VERTEX; - } - ret = true; - break; - case SCE_SELECT_EDGE: - if (use_extend == 0 || em->selectmode == 0) { - em->selectmode = SCE_SELECT_EDGE; - } - ret = true; - break; - case SCE_SELECT_FACE: - if (use_extend == 0 || em->selectmode == 0) { - em->selectmode = SCE_SELECT_FACE; - } - ret = true; - break; - default: - BLI_assert(0); - break; - } - - if (ret == true) { - ts->selectmode = em->selectmode; - em = NULL; - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *ob_iter = objects[ob_index]; - BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter); - em_iter->selectmode = ts->selectmode; - EDBM_selectmode_set(em_iter); - DEG_id_tag_update(ob_iter->data, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data); - } - WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL); - DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); - } - - MEM_freeN(objects); - return ret; -} - -bool EDBM_selectmode_set_multi(bContext *C, const short selectmode) -{ - BLI_assert(selectmode != 0); - bool changed = false; - - { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = NULL; - if (obedit && obedit->type == OB_MESH) { - em = BKE_editmesh_from_object(obedit); - } - if (em == NULL) { - return changed; - } - } - - ViewLayer *view_layer = CTX_data_view_layer(C); - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = scene->toolsettings; - - if (ts->selectmode != selectmode) { - ts->selectmode = selectmode; - changed = true; - } - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - scene, view_layer, CTX_wm_view3d(C), &objects_len); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *ob_iter = objects[ob_index]; - BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter); - if (em_iter->selectmode != ts->selectmode) { - em_iter->selectmode = ts->selectmode; - EDBM_selectmode_set(em_iter); - DEG_id_tag_update(ob_iter->data, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data); - changed = true; - } - } - MEM_freeN(objects); - - if (changed) { - WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL); - DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); - } - return changed; -} - -bool EDBM_selectmode_disable(Scene *scene, - BMEditMesh *em, - const short selectmode_disable, - const short selectmode_fallback) -{ - /* note essential, but switch out of vertex mode since the - * selected regions won't be nicely isolated after flushing */ - if (em->selectmode & selectmode_disable) { - if (em->selectmode == selectmode_disable) { - em->selectmode = selectmode_fallback; - } - else { - em->selectmode &= ~selectmode_disable; - } - scene->toolsettings->selectmode = em->selectmode; - EDBM_selectmode_set(em); - - WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, scene); - - return true; - } - return false; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Toggle - * \{ */ - -bool EDBM_deselect_by_material(BMEditMesh *em, const short index, const bool select) -{ - BMIter iter; - BMFace *efa; - bool changed = false; - - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - continue; - } - if (efa->mat_nr == index) { - changed = true; - BM_face_select_set(em->bm, efa, select); - } - } - return changed; -} - -void EDBM_select_toggle_all(BMEditMesh *em) /* exported for UV */ -{ - if (em->bm->totvertsel || em->bm->totedgesel || em->bm->totfacesel) { - EDBM_flag_disable_all(em, BM_ELEM_SELECT); - } - else { - EDBM_flag_enable_all(em, BM_ELEM_SELECT); - } -} - -void EDBM_select_swap(BMEditMesh *em) /* exported for UV */ -{ - BMIter iter; - BMVert *eve; - BMEdge *eed; - BMFace *efa; - - if (em->bm->selectmode & SCE_SELECT_VERTEX) { - BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { - continue; - } - BM_vert_select_set(em->bm, eve, !BM_elem_flag_test(eve, BM_ELEM_SELECT)); - } - } - else if (em->selectmode & SCE_SELECT_EDGE) { - BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - continue; - } - BM_edge_select_set(em->bm, eed, !BM_elem_flag_test(eed, BM_ELEM_SELECT)); - } - } - else { - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - continue; - } - BM_face_select_set(em->bm, efa, !BM_elem_flag_test(efa, BM_ELEM_SELECT)); - } - } -} - -bool EDBM_mesh_deselect_all_multi_ex(struct Base **bases, const uint bases_len) -{ - bool changed_multi = false; - for (uint base_index = 0; base_index < bases_len; base_index++) { - Base *base_iter = bases[base_index]; - Object *ob_iter = base_iter->object; - BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter); - - if (em_iter->bm->totvertsel == 0) { - continue; - } - - EDBM_flag_disable_all(em_iter, BM_ELEM_SELECT); - DEG_id_tag_update(ob_iter->data, ID_RECALC_SELECT); - changed_multi = true; - } - return changed_multi; -} - -bool EDBM_mesh_deselect_all_multi(struct bContext *C) -{ - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ViewContext vc; - ED_view3d_viewcontext_init(C, &vc, depsgraph); - uint bases_len = 0; - Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data( - vc.scene, vc.view_layer, vc.v3d, &bases_len); - bool changed_multi = EDBM_mesh_deselect_all_multi_ex(bases, bases_len); - MEM_freeN(bases); - return changed_multi; -} - -bool EDBM_selectmode_disable_multi_ex(Scene *scene, - struct Base **bases, - const uint bases_len, - const short selectmode_disable, - const short selectmode_fallback) -{ - bool changed_multi = false; - for (uint base_index = 0; base_index < bases_len; base_index++) { - Base *base_iter = bases[base_index]; - Object *ob_iter = base_iter->object; - BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter); - - if (EDBM_selectmode_disable(scene, em_iter, selectmode_disable, selectmode_fallback)) { - changed_multi = true; - } - } - return changed_multi; -} - -bool EDBM_selectmode_disable_multi(struct bContext *C, - const short selectmode_disable, - const short selectmode_fallback) -{ - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene = CTX_data_scene(C); - ViewContext vc; - ED_view3d_viewcontext_init(C, &vc, depsgraph); - uint bases_len = 0; - Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data( - vc.scene, vc.view_layer, NULL, &bases_len); - bool changed_multi = EDBM_selectmode_disable_multi_ex( - scene, bases, bases_len, selectmode_disable, selectmode_fallback); - MEM_freeN(bases); - return changed_multi; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Interior Faces - * - * Overview of the algorithm: - * - Groups faces surrounded by edges with 3+ faces using them. - * - Calculates a cost of each face group comparing its angle with the faces - * connected to its non-manifold edges. - * - Mark the face group as interior, and mark connected face groups for recalculation. - * - Continue to remove the face groups with the highest 'cost'. - * - * \{ */ - -struct BMFaceLink { - struct BMFaceLink *next, *prev; - BMFace *face; - float area; -}; - -static bool bm_interior_loop_filter_fn(const BMLoop *l, void *UNUSED(user_data)) -{ - if (BM_elem_flag_test(l->e, BM_ELEM_TAG)) { - return false; - } - return true; -} -static bool bm_interior_edge_is_manifold_except_face_index(BMEdge *e, - int face_index, - BMLoop *r_l_pair[2]) -{ - - BMLoop *l_iter = e->l; - int loop_index = 0; - do { - BMFace *f = l_iter->f; - int i = BM_elem_index_get(f); - if (!ELEM(i, -1, face_index)) { - if (loop_index == 2) { - return false; - } - r_l_pair[loop_index++] = l_iter; - } - } while ((l_iter = l_iter->radial_next) != e->l); - return (loop_index == 2); -} - -/** - * Calculate the cost of the face group. - * A higher value means it's more likely to remove first. - */ -static float bm_interior_face_group_calc_cost(ListBase *ls, const float *edge_lengths) -{ - /* Dividing by the area is important so larger face groups (which will become the outer shell) - * aren't detected as having a high cost. */ - float area = 0.0f; - float cost = 0.0f; - bool found = false; - LISTBASE_FOREACH (struct BMFaceLink *, f_link, ls) { - BMFace *f = f_link->face; - area += f_link->area; - int i = BM_elem_index_get(f); - BLI_assert(i != -1); - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - if (BM_elem_flag_test(l_iter->e, BM_ELEM_TAG)) { - float cost_test = 0.0f; - int cost_count = 0; - /* All other faces. */ - BMLoop *l_radial_iter = l_iter; - do { - int i_other = BM_elem_index_get(l_radial_iter->f); - if (!ELEM(i_other, -1, i)) { - float angle = angle_normalized_v3v3(f->no, l_radial_iter->f->no); - /* Ignore face direction since in the case on non-manifold faces connecting edges, - * the face flipping may not be meaningful. */ - if (angle > DEG2RADF(90)) { - angle = DEG2RADF(180) - angle; - } - /* Avoid calculating it inline, pass in pre-calculated edge lengths. */ -#if 0 - cost_test += BM_edge_calc_length(l_iter->e) * angle; -#else - BLI_assert(edge_lengths[BM_elem_index_get(l_iter->e)] != -1.0f); - cost_test += edge_lengths[BM_elem_index_get(l_iter->e)] * angle; -#endif - cost_count += 1; - } - } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter); - - if (cost_count >= 2) { - cost += cost_test; - found = true; - } - } - } while ((l_iter = l_iter->next) != l_first); - } - return found ? cost / area : FLT_MAX; -} - -bool EDBM_select_interior_faces(BMEditMesh *em) -{ - BMesh *bm = em->bm; - BMIter iter; - bool changed = false; - - float *edge_lengths = MEM_mallocN(sizeof(*edge_lengths) * bm->totedge, __func__); - - { - bool has_nonmanifold = false; - BMEdge *e; - int i; - BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) { - const bool is_over = BM_edge_face_count_is_over(e, 2); - if (is_over) { - BM_elem_flag_enable(e, BM_ELEM_TAG); - has_nonmanifold = true; - edge_lengths[i] = BM_edge_calc_length(e); - } - else { - BM_elem_flag_disable(e, BM_ELEM_TAG); - edge_lengths[i] = -1.0; - } - - BM_elem_index_set(e, i); /* set_inline */ - } - bm->elem_index_dirty &= ~BM_EDGE; - - if (has_nonmanifold == false) { - MEM_freeN(edge_lengths); - return false; - } - } - - /* group vars */ - int *fgroup_array; - int(*fgroup_index)[2]; - int fgroup_len; - - fgroup_array = MEM_mallocN(sizeof(*fgroup_array) * bm->totface, __func__); - fgroup_len = BM_mesh_calc_face_groups( - bm, fgroup_array, &fgroup_index, bm_interior_loop_filter_fn, NULL, NULL, 0, BM_EDGE); - - int *fgroup_recalc_stack = MEM_mallocN(sizeof(*fgroup_recalc_stack) * fgroup_len, __func__); - STACK_DECLARE(fgroup_recalc_stack); - STACK_INIT(fgroup_recalc_stack, fgroup_len); - - BM_mesh_elem_table_ensure(bm, BM_FACE); - - { - BMFace *f; - BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) { - BM_elem_index_set(f, -1); /* set_dirty! */ - } - } - bm->elem_index_dirty |= BM_FACE; - - ListBase *fgroup_listbase = MEM_callocN(sizeof(*fgroup_listbase) * fgroup_len, __func__); - struct BMFaceLink *f_link_array = MEM_callocN(sizeof(*f_link_array) * bm->totface, __func__); - - for (int i = 0; i < fgroup_len; i++) { - const int fg_sta = fgroup_index[i][0]; - const int fg_len = fgroup_index[i][1]; - for (int j = 0; j < fg_len; j++) { - const int face_index = fgroup_array[fg_sta + j]; - BMFace *f = BM_face_at_index(bm, face_index); - BM_elem_index_set(f, i); - - struct BMFaceLink *f_link = &f_link_array[face_index]; - f_link->face = f; - f_link->area = BM_face_calc_area(f); - BLI_addtail(&fgroup_listbase[i], f_link); - } - } - - MEM_freeN(fgroup_array); - MEM_freeN(fgroup_index); - - Heap *fgroup_heap = BLI_heap_new_ex(fgroup_len); - HeapNode **fgroup_table = MEM_mallocN(sizeof(*fgroup_table) * fgroup_len, __func__); - bool *fgroup_dirty = MEM_callocN(sizeof(*fgroup_dirty) * fgroup_len, __func__); - - for (int i = 0; i < fgroup_len; i++) { - const float cost = bm_interior_face_group_calc_cost(&fgroup_listbase[i], edge_lengths); - if (cost != FLT_MAX) { - fgroup_table[i] = BLI_heap_insert(fgroup_heap, -cost, POINTER_FROM_INT(i)); - } - else { - fgroup_table[i] = NULL; - } - } - - /* Avoid re-running cost calculations for large face-groups which will end up forming the - * outer shell and not be considered interior. - * As these face groups become increasingly bigger - their chance of being considered - * interior reduces as does the time to calculate their cost. - * - * This delays recalculating them until they are considered can dates to remove - * which becomes less and less likely as they increase in area. */ - -#define USE_DELAY_FACE_GROUP_COST_CALC - - while (true) { - -#if defined(USE_DELAY_FACE_GROUP_COST_CALC) - while (!BLI_heap_is_empty(fgroup_heap)) { - HeapNode *node_min = BLI_heap_top(fgroup_heap); - const int i = POINTER_AS_INT(BLI_heap_node_ptr(node_min)); - if (fgroup_dirty[i]) { - const float cost = bm_interior_face_group_calc_cost(&fgroup_listbase[i], edge_lengths); - if (cost != FLT_MAX) { - /* The cost may have improves (we may be able to skip this), - * however the cost should _never_ make this a choice. */ - BLI_assert(-BLI_heap_node_value(node_min) >= cost); - BLI_heap_node_value_update(fgroup_heap, fgroup_table[i], -cost); - } - else { - BLI_heap_remove(fgroup_heap, fgroup_table[i]); - fgroup_table[i] = NULL; - } - fgroup_dirty[i] = false; - } - else { - break; - } - } -#endif - - if (BLI_heap_is_empty(fgroup_heap)) { - break; - } - - const int i_min = POINTER_AS_INT(BLI_heap_pop_min(fgroup_heap)); - BLI_assert(fgroup_table[i_min] != NULL); - BLI_assert(fgroup_dirty[i_min] == false); - fgroup_table[i_min] = NULL; - changed = true; - - struct BMFaceLink *f_link; - while ((f_link = BLI_pophead(&fgroup_listbase[i_min]))) { - BMFace *f = f_link->face; - BM_face_select_set(bm, f, true); - BM_elem_index_set(f, -1); /* set-dirty */ - - BMLoop *l_iter, *l_first; - - /* Loop over edges face edges, merging groups which are no longer separated - * by non-manifold edges (when manifold check ignores faces from this group). */ - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - BMLoop *l_pair[2]; - if (bm_interior_edge_is_manifold_except_face_index(l_iter->e, i_min, l_pair)) { - BM_elem_flag_disable(l_iter->e, BM_ELEM_TAG); - - int i_a = BM_elem_index_get(l_pair[0]->f); - int i_b = BM_elem_index_get(l_pair[1]->f); - if (i_a != i_b) { - /* Only for predictable results that don't depend on the order of radial loops, - * not essential. */ - if (i_a > i_b) { - SWAP(int, i_a, i_b); - } - - /* Merge the groups. */ - LISTBASE_FOREACH (LinkData *, n, &fgroup_listbase[i_b]) { - BMFace *f_iter = n->data; - BM_elem_index_set(f_iter, i_a); - } - BLI_movelisttolist(&fgroup_listbase[i_a], &fgroup_listbase[i_b]); - - /* This may have been added to 'fgroup_recalc_stack', instead of removing it, - * just check the heap node isn't NULL before recalculating. */ - BLI_heap_remove(fgroup_heap, fgroup_table[i_b]); - fgroup_table[i_b] = NULL; - /* Keep the dirty flag as-is for 'i_b', because it may be in the 'fgroup_recalc_stack' - * and we don't want to add it again. - * Instead rely on the 'fgroup_table[i_b]' being NULL as a secondary check. */ - - if (fgroup_dirty[i_a] == false) { - BLI_assert(fgroup_table[i_a] != NULL); - STACK_PUSH(fgroup_recalc_stack, i_a); - fgroup_dirty[i_a] = true; - } - } - } - - /* Mark all connected groups for re-calculation. */ - BMLoop *l_radial_iter = l_iter->radial_next; - if (l_radial_iter != l_iter) { - do { - int i_other = BM_elem_index_get(l_radial_iter->f); - if (!ELEM(i_other, -1, i_min)) { - if ((fgroup_table[i_other] != NULL) && (fgroup_dirty[i_other] == false)) { -#if !defined(USE_DELAY_FACE_GROUP_COST_CALC) - STACK_PUSH(fgroup_recalc_stack, i_other); -#endif - fgroup_dirty[i_other] = true; - } - } - } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter); - } - - } while ((l_iter = l_iter->next) != l_first); - } - - for (int index = 0; index < STACK_SIZE(fgroup_recalc_stack); index++) { - const int i = fgroup_recalc_stack[index]; - if (fgroup_table[i] != NULL && fgroup_dirty[i] == true) { - /* First update edge tags. */ - const float cost = bm_interior_face_group_calc_cost(&fgroup_listbase[i], edge_lengths); - if (cost != FLT_MAX) { - BLI_heap_node_value_update(fgroup_heap, fgroup_table[i], -cost); - } - else { - BLI_heap_remove(fgroup_heap, fgroup_table[i]); - fgroup_table[i] = NULL; - } - } - fgroup_dirty[i] = false; - } - STACK_CLEAR(fgroup_recalc_stack); - } - - MEM_freeN(edge_lengths); - MEM_freeN(f_link_array); - MEM_freeN(fgroup_listbase); - MEM_freeN(fgroup_recalc_stack); - MEM_freeN(fgroup_table); - MEM_freeN(fgroup_dirty); - - BLI_heap_free(fgroup_heap, NULL); - - return changed; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Linked Operator - * - * Support delimiting on different edge properties. - * \{ */ - -/* so we can have last-used default depend on selection mode (rare exception!) */ -#define USE_LINKED_SELECT_DEFAULT_HACK - -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) -{ - 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; -} - -#ifdef USE_LINKED_SELECT_DEFAULT_HACK -/** - * Gets the default from the operator fallback to own last-used value - * (selected based on mode) - */ -static int select_linked_delimit_default_from_op(wmOperator *op, const int select_mode) -{ - static char delimit_last_store[2] = {0, BMO_DELIM_SEAM}; - int delimit_last_index = (select_mode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) == 0; - char *delimit_last = &delimit_last_store[delimit_last_index]; - PropertyRNA *prop_delimit = RNA_struct_find_property(op->ptr, "delimit"); - int delimit; - - if (RNA_property_is_set(op->ptr, prop_delimit)) { - delimit = RNA_property_enum_get(op->ptr, prop_delimit); - *delimit_last = delimit; - } - else { - delimit = *delimit_last; - RNA_property_enum_set(op->ptr, prop_delimit, delimit); - } - return delimit; -} -#endif - -static void select_linked_delimit_validate(BMesh *bm, int *delimit) -{ - if ((*delimit) & BMO_DELIM_UV) { - if (!CustomData_has_layer(&bm->ldata, CD_MLOOPUV)) { - (*delimit) &= ~BMO_DELIM_UV; - } - } -} - -static void select_linked_delimit_begin(BMesh *bm, int delimit) -{ - struct DelimitData delimit_data = {0}; - - 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); - - { - BMIter iter; - BMEdge *e; - - 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_edge_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) -{ - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - -#ifdef USE_LINKED_SELECT_DEFAULT_HACK - const int delimit_init = select_linked_delimit_default_from_op(op, - scene->toolsettings->selectmode); -#else - const int delimit_init = RNA_enum_get(op->ptr, "delimit"); -#endif - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - scene, view_layer, CTX_wm_view3d(C), &objects_len); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMesh *bm = em->bm; - BMIter iter; - BMWalker walker; - - int delimit = delimit_init; - - select_linked_delimit_validate(bm, &delimit); - - if (delimit) { - select_linked_delimit_begin(em->bm, delimit); - } - - if (em->selectmode & SCE_SELECT_VERTEX) { - BMVert *v; - - 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)); - } - - /* exclude all delimited verts */ - if (delimit) { - BMEdge *e; - BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { - if (!BMO_edge_flag_test(bm, e, BMO_ELE_TAG)) { - /* Check the edge for selected faces, - * this supports stepping off isolated vertices which would otherwise be ignored. */ - if (BM_edge_is_any_face_flag_test(e, BM_ELEM_SELECT)) { - BM_elem_flag_disable(e->v1, BM_ELEM_TAG); - BM_elem_flag_disable(e->v2, BM_ELEM_TAG); - } - } - } - } - - 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_end(&walker); - - EDBM_selectmode_flush(em); - } - else if (em->selectmode & SCE_SELECT_EDGE) { - BMEdge *e; - - if (delimit) { - BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { - /* Check the edge for selected faces, - * this supports stepping off isolated edges which would otherwise be ignored. */ - BM_elem_flag_set(e, - BM_ELEM_TAG, - (BM_elem_flag_test(e, BM_ELEM_SELECT) && - (BMO_edge_flag_test(bm, e, BMO_ELE_TAG) || - !BM_edge_is_any_face_flag_test(e, BM_ELEM_SELECT)))); - } - } - else { - 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); - - 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); - - EDBM_selectmode_flush(em); - } - else { - BMFace *f; - - 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, - 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 (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); - } - - if (delimit) { - select_linked_delimit_end(em); - } - - DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); - } - - MEM_freeN(objects); - - return OPERATOR_FINISHED; -} - -void MESH_OT_select_linked(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Select Linked All"; - ot->idname = "MESH_OT_select_linked"; - ot->description = "Select all vertices connected to the current selection"; - - /* api callbacks */ - ot->exec = edbm_select_linked_exec; - ot->poll = ED_operator_editmesh; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - prop = RNA_def_enum_flag(ot->srna, - "delimit", - rna_enum_mesh_delimit_mode_items, - BMO_DELIM_SEAM, - "Delimit", - "Delimit selected region"); -#ifdef USE_LINKED_SELECT_DEFAULT_HACK - RNA_def_property_flag(prop, PROP_SKIP_SAVE); -#else - UNUSED_VARS(prop); -#endif -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Linked (Cursor Pick) Operator - * \{ */ - -static int edbm_select_linked_pick_exec(bContext *C, wmOperator *op); - -static void edbm_select_linked_pick_ex(BMEditMesh *em, BMElem *ele, bool sel, int delimit) -{ - BMesh *bm = em->bm; - BMWalker walker; - - select_linked_delimit_validate(bm, &delimit); - - if (delimit) { - select_linked_delimit_begin(bm, delimit); - } - - /* NOTE: logic closely matches #edbm_select_linked_exec, keep in sync. */ - - if (ele->head.htype == BM_VERT) { - BMVert *eve = (BMVert *)ele; - - 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 (ele->head.htype == BM_EDGE) { - BMEdge *eed = (BMEdge *)ele; - - 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 (ele->head.htype == BM_FACE) { - BMFace *efa = (BMFace *)ele; - - 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) -{ - ViewContext vc; - Base *basact = NULL; - BMVert *eve; - BMEdge *eed; - BMFace *efa; - const bool sel = !RNA_boolean_get(op->ptr, "deselect"); - int index; - - if (RNA_struct_property_is_set(op->ptr, "index")) { - return edbm_select_linked_pick_exec(C, op); - } - - /* #unified_findnearest needs OpenGL. */ - view3d_operator_needs_opengl(C); - - /* setup view context for argument to callbacks */ - em_setup_viewcontext(C, &vc); - - uint bases_len; - Base **bases = BKE_view_layer_array_from_bases_in_edit_mode( - vc.scene, vc.view_layer, vc.v3d, &bases_len); - - { - bool has_edges = false; - for (uint base_index = 0; base_index < bases_len; base_index++) { - Object *ob_iter = bases[base_index]->object; - ED_view3d_viewcontext_init_object(&vc, ob_iter); - if (vc.em->bm->totedge) { - has_edges = true; - } - } - if (has_edges == false) { - MEM_freeN(bases); - return OPERATOR_CANCELLED; - } - } - - vc.mval[0] = event->mval[0]; - vc.mval[1] = event->mval[1]; - - /* return warning! */ - { - int base_index = -1; - const bool ok = unified_findnearest(&vc, bases, bases_len, &base_index, &eve, &eed, &efa); - if (!ok) { - MEM_freeN(bases); - return OPERATOR_CANCELLED; - } - basact = bases[base_index]; - } - - ED_view3d_viewcontext_init_object(&vc, basact->object); - BMEditMesh *em = vc.em; - BMesh *bm = em->bm; - -#ifdef USE_LINKED_SELECT_DEFAULT_HACK - int delimit = select_linked_delimit_default_from_op(op, vc.scene->toolsettings->selectmode); -#else - int delimit = RNA_enum_get(op->ptr, "delimit"); -#endif - - BMElem *ele = EDBM_elem_from_selectmode(em, eve, eed, efa); - - edbm_select_linked_pick_ex(em, ele, sel, delimit); - - /* To support redo. */ - { - /* Note that the `base_index` can't be used as the index depends on the 3D Viewport - * which might not be available on redo. */ - BM_mesh_elem_index_ensure(bm, ele->head.htype); - int object_index; - index = EDBM_elem_to_index_any_multi(vc.scene, vc.view_layer, em, ele, &object_index); - BLI_assert(object_index >= 0); - RNA_int_set(op->ptr, "object_index", object_index); - RNA_int_set(op->ptr, "index", index); - } - - DEG_id_tag_update(basact->object->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, basact->object->data); - - MEM_freeN(bases); - return OPERATOR_FINISHED; -} - -static int edbm_select_linked_pick_exec(bContext *C, wmOperator *op) -{ - Object *obedit = NULL; - BMElem *ele; - - { - const Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - /* Intentionally wrap negative values so the lookup fails. */ - const uint object_index = (uint)RNA_int_get(op->ptr, "object_index"); - const uint index = (uint)RNA_int_get(op->ptr, "index"); - ele = EDBM_elem_from_index_any_multi(scene, view_layer, object_index, index, &obedit); - } - - if (ele == NULL) { - return OPERATOR_CANCELLED; - } - - BMEditMesh *em = BKE_editmesh_from_object(obedit); - const bool sel = !RNA_boolean_get(op->ptr, "deselect"); - -#ifdef USE_LINKED_SELECT_DEFAULT_HACK - int delimit = select_linked_delimit_default_from_op(op, em->selectmode); -#else - int delimit = RNA_enum_get(op->ptr, "delimit"); -#endif - - edbm_select_linked_pick_ex(em, ele, sel, delimit); - - DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); - - return OPERATOR_FINISHED; -} - -void MESH_OT_select_linked_pick(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Select Linked"; - ot->idname = "MESH_OT_select_linked_pick"; - ot->description = "(De)select all vertices linked to the edge under the mouse cursor"; - - /* 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", ""); - prop = RNA_def_enum_flag(ot->srna, - "delimit", - rna_enum_mesh_delimit_mode_items, - BMO_DELIM_SEAM, - "Delimit", - "Delimit selected region"); -#ifdef USE_LINKED_SELECT_DEFAULT_HACK - RNA_def_property_flag(prop, PROP_SKIP_SAVE); -#endif - - /* use for redo */ - prop = RNA_def_int(ot->srna, "object_index", -1, -1, INT_MAX, "", "", 0, INT_MAX); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - prop = RNA_def_int(ot->srna, "index", -1, -1, INT_MAX, "", "", 0, INT_MAX); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Face by Sides Operator - * \{ */ - -static int edbm_select_face_by_sides_exec(bContext *C, wmOperator *op) -{ - const Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - uint objects_len = 0; - const bool extend = RNA_boolean_get(op->ptr, "extend"); - const int numverts = RNA_int_get(op->ptr, "number"); - const int type = RNA_enum_get(op->ptr, "type"); - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - scene, view_layer, CTX_wm_view3d(C), &objects_len); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMFace *efa; - BMIter iter; - - if (!extend) { - EDBM_flag_disable_all(em, BM_ELEM_SELECT); - } - - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - bool select; - - switch (type) { - case 0: - select = (efa->len < numverts); - break; - case 1: - select = (efa->len == numverts); - break; - case 2: - select = (efa->len > numverts); - break; - case 3: - select = (efa->len != numverts); - break; - default: - BLI_assert(0); - select = false; - break; - } - - if (select) { - BM_face_select_set(em->bm, efa, true); - } - } - - EDBM_selectmode_flush(em); - - DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); - } - - MEM_freeN(objects); - return OPERATOR_FINISHED; -} - -void MESH_OT_select_face_by_sides(wmOperatorType *ot) -{ - static const EnumPropertyItem type_items[] = { - {0, "LESS", 0, "Less Than", ""}, - {1, "EQUAL", 0, "Equal To", ""}, - {2, "GREATER", 0, "Greater Than", ""}, - {3, "NOTEQUAL", 0, "Not Equal To", ""}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Select Faces by Sides"; - ot->description = "Select vertices or faces by the number of polygon sides"; - ot->idname = "MESH_OT_select_face_by_sides"; - - /* api callbacks */ - ot->exec = edbm_select_face_by_sides_exec; - ot->poll = ED_operator_editmesh; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_int(ot->srna, "number", 4, 3, INT_MAX, "Number of Vertices", "", 3, INT_MAX); - RNA_def_enum(ot->srna, "type", type_items, 1, "Type", "Type of comparison to make"); - RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend the selection"); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Loose Operator - * \{ */ - -static int edbm_select_loose_exec(bContext *C, wmOperator *op) -{ - const Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - const bool extend = RNA_boolean_get(op->ptr, "extend"); - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - scene, view_layer, CTX_wm_view3d(C), &objects_len); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMesh *bm = em->bm; - BMIter iter; - - if (!extend) { - EDBM_flag_disable_all(em, BM_ELEM_SELECT); - } - - if (em->selectmode & SCE_SELECT_VERTEX) { - BMVert *eve; - BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { - if (!eve->e) { - BM_vert_select_set(bm, eve, true); - } - } - } - - if (em->selectmode & SCE_SELECT_EDGE) { - BMEdge *eed; - BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { - if (BM_edge_is_wire(eed)) { - BM_edge_select_set(bm, eed, true); - } - } - } - - if (em->selectmode & SCE_SELECT_FACE) { - BMFace *efa; - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - BMIter liter; - BMLoop *l; - bool is_loose = true; - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (!BM_edge_is_boundary(l->e)) { - is_loose = false; - break; - } - } - if (is_loose) { - BM_face_select_set(bm, efa, true); - } - } - } - - EDBM_selectmode_flush(em); - - DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); - } - - MEM_freeN(objects); - - return OPERATOR_FINISHED; -} - -void MESH_OT_select_loose(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Loose Geometry"; - ot->description = "Select loose geometry based on the selection mode"; - ot->idname = "MESH_OT_select_loose"; - - /* api callbacks */ - ot->exec = edbm_select_loose_exec; - ot->poll = ED_operator_editmesh; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* props */ - RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Mirror Operator - * \{ */ - -static int edbm_select_mirror_exec(bContext *C, wmOperator *op) -{ - const Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - const int axis_flag = RNA_enum_get(op->ptr, "axis"); - const bool extend = RNA_boolean_get(op->ptr, "extend"); - Object *obedit_active = CTX_data_edit_object(C); - BMEditMesh *em_active = BKE_editmesh_from_object(obedit_active); - const int select_mode = em_active->bm->selectmode; - int tot_mirr = 0, tot_fail = 0; - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - scene, view_layer, CTX_wm_view3d(C), &objects_len); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - - if (em->bm->totvertsel == 0) { - continue; - } - - int tot_mirr_iter = 0, tot_fail_iter = 0; - - for (int axis = 0; axis < 3; axis++) { - if ((1 << axis) & axis_flag) { - EDBM_select_mirrored(em, obedit->data, axis, extend, &tot_mirr_iter, &tot_fail_iter); - } - } - - if (tot_mirr_iter) { - EDBM_selectmode_flush(em); - - DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); - } - - tot_fail += tot_fail_iter; - tot_mirr += tot_mirr_iter; - } - MEM_freeN(objects); - - if (tot_mirr || tot_fail) { - ED_mesh_report_mirror_ex(op, tot_mirr, tot_fail, select_mode); - } - return OPERATOR_FINISHED; -} - -void MESH_OT_select_mirror(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Mirror"; - ot->description = "Select mesh items at mirrored locations"; - ot->idname = "MESH_OT_select_mirror"; - - /* api callbacks */ - ot->exec = edbm_select_mirror_exec; - ot->poll = ED_operator_editmesh; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* props */ - RNA_def_enum_flag(ot->srna, "axis", rna_enum_axis_flag_xyz_items, (1 << 0), "Axis", ""); - - RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the existing selection"); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select More Operator - * \{ */ - -static int edbm_select_more_exec(bContext *C, wmOperator *op) -{ - const Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - const bool use_face_step = RNA_boolean_get(op->ptr, "use_face_step"); - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - scene, view_layer, CTX_wm_view3d(C), &objects_len); - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMesh *bm = em->bm; - - if ((bm->totvertsel == 0) && (bm->totedgesel == 0) && (bm->totfacesel == 0)) { - continue; - } - - EDBM_select_more(em, use_face_step); - DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); - } - - MEM_freeN(objects); - return OPERATOR_FINISHED; -} - -void MESH_OT_select_more(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select More"; - ot->idname = "MESH_OT_select_more"; - ot->description = "Select more vertices, edges or faces connected to initial selection"; - - /* api callbacks */ - ot->exec = edbm_select_more_exec; - ot->poll = ED_operator_editmesh; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_boolean( - ot->srna, "use_face_step", true, "Face Step", "Connected faces (instead of edges)"); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select More Operator - * \{ */ - -static int edbm_select_less_exec(bContext *C, wmOperator *op) -{ - const Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - const bool use_face_step = RNA_boolean_get(op->ptr, "use_face_step"); - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - scene, view_layer, CTX_wm_view3d(C), &objects_len); - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMesh *bm = em->bm; - - if ((bm->totvertsel == 0) && (bm->totedgesel == 0) && (bm->totfacesel == 0)) { - continue; - } - - EDBM_select_less(em, use_face_step); - DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); - } - - MEM_freeN(objects); - return OPERATOR_FINISHED; -} - -void MESH_OT_select_less(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Less"; - ot->idname = "MESH_OT_select_less"; - ot->description = "Deselect vertices, edges or faces at the boundary of each selection region"; - - /* api callbacks */ - ot->exec = edbm_select_less_exec; - ot->poll = ED_operator_editmesh; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_boolean( - ot->srna, "use_face_step", true, "Face Step", "Connected faces (instead of edges)"); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select N'th Operator - * \{ */ - -/** - * Check if we're connected to another selected edge. - */ -static bool bm_edge_is_select_isolated(BMEdge *e) -{ - BMIter viter; - BMVert *v; - - BM_ITER_ELEM (v, &viter, e, BM_VERTS_OF_EDGE) { - BMIter eiter; - BMEdge *e_other; - - BM_ITER_ELEM (e_other, &eiter, v, BM_EDGES_OF_VERT) { - if ((e_other != e) && BM_elem_flag_test(e_other, BM_ELEM_SELECT)) { - return false; - } - } - } - return true; -} - -/* 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, - const struct CheckerIntervalParams *op_params, - BMHeader *h_act) -{ - BMElem *ele; - BMesh *bm = em->bm; - BMWalker walker; - BMIter iter; - int walktype = 0, itertype = 0, flushtype = 0; - short mask_vert = 0, mask_edge = 0, mask_face = 0; - - /* No active element from which to start - nothing to do */ - if (h_act == NULL) { - return; - } - - /* Determine which type of iter, walker, and select flush to use - * based on type of the elements being deselected */ - switch (h_act->htype) { - case BM_VERT: - itertype = BM_VERTS_OF_MESH; - walktype = BMW_CONNECTED_VERTEX; - flushtype = SCE_SELECT_VERTEX; - mask_vert = BMO_ELE_TAG; - break; - case BM_EDGE: - /* When an edge has no connected-selected edges, - * use face-stepping (supports edge-rings) */ - itertype = BM_EDGES_OF_MESH; - walktype = bm_edge_is_select_isolated((BMEdge *)h_act) ? BMW_FACE_SHELL : BMW_VERT_SHELL; - flushtype = SCE_SELECT_EDGE; - mask_edge = BMO_ELE_TAG; - break; - case BM_FACE: - itertype = BM_FACES_OF_MESH; - walktype = BMW_ISLAND; - flushtype = SCE_SELECT_FACE; - mask_face = BMO_ELE_TAG; - break; - } - - /* grr, shouldn't need to alloc BMO flags here */ - BM_mesh_elem_toolflags_ensure(bm); - - /* Walker restrictions uses BMO flags, not header flags, - * so transfer BM_ELEM_SELECT from HFlags onto a BMO flag layer. */ - BMO_push(bm, NULL); - BM_ITER_MESH (ele, &iter, bm, itertype) { - if (BM_elem_flag_test(ele, BM_ELEM_SELECT)) { - BMO_elem_flag_enable(bm, (BMElemF *)ele, BMO_ELE_TAG); - } - } - - /* Walk over selected elements starting at active */ - BMW_init(&walker, - bm, - walktype, - mask_vert, - mask_edge, - mask_face, - BMW_FLAG_NOP, /* don't use BMW_FLAG_TEST_HIDDEN here since we want to desel all */ - BMW_NIL_LAY); - - /* use tag to avoid touching the same verts twice */ - BM_ITER_MESH (ele, &iter, bm, itertype) { - BM_elem_flag_disable(ele, BM_ELEM_TAG); - } - - BLI_assert(walker.order == BMW_BREADTH_FIRST); - 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 */ - const int depth = BMW_current_depth(&walker) - 1; - if (!WM_operator_properties_checker_interval_test(op_params, depth)) { - BM_elem_select_set(bm, ele, false); - } - BM_elem_flag_enable(ele, BM_ELEM_TAG); - } - } - BMW_end(&walker); - - BMO_pop(bm); - - /* Flush selection up */ - EDBM_selectmode_flush_ex(em, flushtype); -} - -static void deselect_nth_active(BMEditMesh *em, BMVert **r_eve, BMEdge **r_eed, BMFace **r_efa) -{ - BMIter iter; - BMElem *ele; - - *r_eve = NULL; - *r_eed = NULL; - *r_efa = NULL; - - EDBM_selectmode_flush(em); - ele = BM_mesh_active_elem_get(em->bm); - - if (ele && BM_elem_flag_test(ele, BM_ELEM_SELECT)) { - switch (ele->head.htype) { - case BM_VERT: - *r_eve = (BMVert *)ele; - return; - case BM_EDGE: - *r_eed = (BMEdge *)ele; - return; - case BM_FACE: - *r_efa = (BMFace *)ele; - return; - } - } - - if (em->selectmode & SCE_SELECT_VERTEX) { - BMVert *v; - BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { - *r_eve = v; - return; - } - } - } - else if (em->selectmode & SCE_SELECT_EDGE) { - BMEdge *e; - BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { - *r_eed = e; - return; - } - } - } - else if (em->selectmode & SCE_SELECT_FACE) { - BMFace *f = BM_mesh_active_face_get(em->bm, true, false); - if (f && BM_elem_flag_test(f, BM_ELEM_SELECT)) { - *r_efa = f; - return; - } - } -} - -static bool edbm_deselect_nth(BMEditMesh *em, const struct CheckerIntervalParams *op_params) -{ - BMVert *v; - BMEdge *e; - BMFace *f; - - deselect_nth_active(em, &v, &e, &f); - - if (v) { - walker_deselect_nth(em, op_params, &v->head); - return true; - } - if (e) { - walker_deselect_nth(em, op_params, &e->head); - return true; - } - if (f) { - walker_deselect_nth(em, op_params, &f->head); - return true; - } - - return false; -} - -static int edbm_select_nth_exec(bContext *C, wmOperator *op) -{ - const Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - struct CheckerIntervalParams op_params; - WM_operator_properties_checker_interval_from_op(op, &op_params); - bool found_active_elt = false; - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - scene, view_layer, CTX_wm_view3d(C), &objects_len); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - - if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) { - continue; - } - - if (edbm_deselect_nth(em, &op_params) == true) { - found_active_elt = true; - EDBM_update(obedit->data, - &(const struct EDBMUpdate_Params){ - .calc_looptri = false, - .calc_normals = false, - .is_destructive = false, - }); - } - } - MEM_freeN(objects); - - if (!found_active_elt) { - BKE_report(op->reports, RPT_ERROR, "Mesh object(s) have no active vertex/edge/face"); - return OPERATOR_CANCELLED; - } - - return OPERATOR_FINISHED; -} - -void MESH_OT_select_nth(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Checker Deselect"; - ot->idname = "MESH_OT_select_nth"; - ot->description = "Deselect every Nth element starting from the active vertex, edge or face"; - - /* api callbacks */ - ot->exec = edbm_select_nth_exec; - ot->poll = ED_operator_editmesh; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - WM_operator_properties_checker_interval(ot, false); -} - -void em_setup_viewcontext(bContext *C, ViewContext *vc) -{ - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ED_view3d_viewcontext_init(C, vc, depsgraph); - - if (vc->obedit) { - vc->em = BKE_editmesh_from_object(vc->obedit); - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Sharp Edges Operator - * \{ */ - -static int edbm_select_sharp_edges_exec(bContext *C, wmOperator *op) -{ - /* Find edges that have exactly two neighboring faces, - * check the angle between those faces, and if angle is - * small enough, select the edge - */ - const float angle_limit_cos = cosf(RNA_float_get(op->ptr, "sharpness")); - - const Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - scene, view_layer, CTX_wm_view3d(C), &objects_len); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMIter iter; - BMEdge *e; - BMLoop *l1, *l2; - - BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) == false && BM_edge_loop_pair(e, &l1, &l2)) { - /* edge has exactly two neighboring faces, check angle */ - const float angle_cos = dot_v3v3(l1->f->no, l2->f->no); - - if (angle_cos < angle_limit_cos) { - BM_edge_select_set(em->bm, e, true); - } - } - } - - if ((em->bm->selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) == 0) { - /* Since we can't select individual edges, select faces connected to them. */ - EDBM_selectmode_convert(em, SCE_SELECT_EDGE, SCE_SELECT_FACE); - } - else { - EDBM_selectmode_flush(em); - } - DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); - } - MEM_freeN(objects); - - return OPERATOR_FINISHED; -} - -void MESH_OT_edges_select_sharp(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Select Sharp Edges"; - ot->description = "Select all sharp enough edges"; - ot->idname = "MESH_OT_edges_select_sharp"; - - /* api callbacks */ - ot->exec = edbm_select_sharp_edges_exec; - ot->poll = ED_operator_editmesh; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* props */ - prop = RNA_def_float_rotation(ot->srna, - "sharpness", - 0, - NULL, - DEG2RADF(0.01f), - DEG2RADF(180.0f), - "Sharpness", - "", - DEG2RADF(1.0f), - DEG2RADF(180.0f)); - RNA_def_property_float_default(prop, DEG2RADF(30.0f)); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Linked Flat Faces Operator - * \{ */ - -static int edbm_select_linked_flat_faces_exec(bContext *C, wmOperator *op) -{ - const Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - scene, view_layer, CTX_wm_view3d(C), &objects_len); - const float angle_limit_cos = cosf(RNA_float_get(op->ptr, "sharpness")); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMesh *bm = em->bm; - - if (bm->totfacesel == 0) { - continue; - } - - BLI_LINKSTACK_DECLARE(stack, BMFace *); - - BMIter iter, liter, liter2; - BMFace *f; - BMLoop *l, *l2; - - BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false); - - BLI_LINKSTACK_INIT(stack); - - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - if ((BM_elem_flag_test(f, BM_ELEM_HIDDEN) != 0) || - (BM_elem_flag_test(f, BM_ELEM_TAG) != 0) || - (BM_elem_flag_test(f, BM_ELEM_SELECT) == 0)) { - continue; - } - - BLI_assert(BLI_LINKSTACK_SIZE(stack) == 0); - - do { - BM_face_select_set(bm, f, true); - - BM_elem_flag_enable(f, BM_ELEM_TAG); - - BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { - BM_ITER_ELEM (l2, &liter2, l, BM_LOOPS_OF_LOOP) { - float angle_cos; - - if (BM_elem_flag_test(l2->f, BM_ELEM_TAG) || - BM_elem_flag_test(l2->f, BM_ELEM_HIDDEN)) { - continue; - } - - angle_cos = dot_v3v3(f->no, l2->f->no); - - if (angle_cos > angle_limit_cos) { - BLI_LINKSTACK_PUSH(stack, l2->f); - } - } - } - } while ((f = BLI_LINKSTACK_POP(stack))); - } - - BLI_LINKSTACK_FREE(stack); - - DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); - } - MEM_freeN(objects); - - return OPERATOR_FINISHED; -} - -void MESH_OT_faces_select_linked_flat(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Select Linked Flat Faces"; - ot->description = "Select linked faces by angle"; - ot->idname = "MESH_OT_faces_select_linked_flat"; - - /* api callbacks */ - ot->exec = edbm_select_linked_flat_faces_exec; - ot->poll = ED_operator_editmesh; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* props */ - prop = RNA_def_float_rotation(ot->srna, - "sharpness", - 0, - NULL, - DEG2RADF(0.01f), - DEG2RADF(180.0f), - "Sharpness", - "", - DEG2RADF(1.0f), - DEG2RADF(180.0f)); - RNA_def_property_float_default(prop, DEG2RADF(1.0f)); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Non-Manifold Operator - * \{ */ - -static int edbm_select_non_manifold_exec(bContext *C, wmOperator *op) -{ - const bool use_extend = RNA_boolean_get(op->ptr, "extend"); - const bool use_wire = RNA_boolean_get(op->ptr, "use_wire"); - const bool use_boundary = RNA_boolean_get(op->ptr, "use_boundary"); - const bool use_multi_face = RNA_boolean_get(op->ptr, "use_multi_face"); - const bool use_non_contiguous = RNA_boolean_get(op->ptr, "use_non_contiguous"); - const bool use_verts = RNA_boolean_get(op->ptr, "use_verts"); - - const Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - scene, view_layer, CTX_wm_view3d(C), &objects_len); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMVert *v; - BMEdge *e; - BMIter iter; - - if (!use_extend) { - EDBM_flag_disable_all(em, BM_ELEM_SELECT); - } - - /* Selects isolated verts, and edges that do not have 2 neighboring - * faces - */ - - if (em->selectmode == SCE_SELECT_FACE) { - BKE_report(op->reports, RPT_ERROR, "Does not work in face selection mode"); - MEM_freeN(objects); - return OPERATOR_CANCELLED; - } - - if (use_verts) { - BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) { - if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { - if (!BM_vert_is_manifold(v)) { - BM_vert_select_set(em->bm, v, true); - } - } - } - } - - if (use_wire || use_boundary || use_multi_face || use_non_contiguous) { - BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { - if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { - 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_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))); - - BM_edge_select_set(em->bm, e, true); - } - } - } - } - - DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); - - EDBM_selectmode_flush(em); - } - MEM_freeN(objects); - - return OPERATOR_FINISHED; -} - -void MESH_OT_select_non_manifold(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Non-Manifold"; - ot->description = "Select all non-manifold vertices or edges"; - ot->idname = "MESH_OT_select_non_manifold"; - - /* api callbacks */ - ot->exec = edbm_select_non_manifold_exec; - ot->poll = ED_operator_editmesh; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* props */ - RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend the selection"); - /* edges */ - RNA_def_boolean(ot->srna, "use_wire", true, "Wire", "Wire edges"); - RNA_def_boolean(ot->srna, "use_boundary", true, "Boundaries", "Boundary edges"); - RNA_def_boolean( - ot->srna, "use_multi_face", true, "Multiple Faces", "Edges shared by more than two faces"); - RNA_def_boolean(ot->srna, - "use_non_contiguous", - true, - "Non Contiguous", - "Edges between faces pointing in alternate directions"); - /* verts */ - RNA_def_boolean( - ot->srna, "use_verts", true, "Vertices", "Vertices connecting multiple face regions"); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Random Operator - * \{ */ - -static int edbm_select_random_exec(bContext *C, wmOperator *op) -{ - const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT); - const float randfac = RNA_float_get(op->ptr, "ratio"); - const int seed = WM_operator_properties_select_random_seed_increment_get(op); - - const Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - scene, view_layer, CTX_wm_view3d(C), &objects_len); - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMIter iter; - int seed_iter = seed; - - /* This gives a consistent result regardless of object order. */ - if (ob_index) { - seed_iter += BLI_ghashutil_strhash_p(obedit->id.name); - } - - if (em->selectmode & SCE_SELECT_VERTEX) { - int elem_map_len = 0; - BMVert **elem_map = MEM_mallocN(sizeof(*elem_map) * em->bm->totvert, __func__); - BMVert *eve; - BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { - if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { - elem_map[elem_map_len++] = eve; - } - } - - BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed_iter); - const int count_select = elem_map_len * randfac; - for (int i = 0; i < count_select; i++) { - BM_vert_select_set(em->bm, elem_map[i], select); - } - MEM_freeN(elem_map); - } - else if (em->selectmode & SCE_SELECT_EDGE) { - int elem_map_len = 0; - BMEdge **elem_map = MEM_mallocN(sizeof(*elem_map) * em->bm->totedge, __func__); - BMEdge *eed; - BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { - if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - elem_map[elem_map_len++] = eed; - } - } - BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed_iter); - const int count_select = elem_map_len * randfac; - for (int i = 0; i < count_select; i++) { - BM_edge_select_set(em->bm, elem_map[i], select); - } - MEM_freeN(elem_map); - } - else { - int elem_map_len = 0; - BMFace **elem_map = MEM_mallocN(sizeof(*elem_map) * em->bm->totface, __func__); - BMFace *efa; - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - elem_map[elem_map_len++] = efa; - } - } - BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed_iter); - const int count_select = elem_map_len * randfac; - for (int i = 0; i < count_select; i++) { - BM_face_select_set(em->bm, elem_map[i], select); - } - MEM_freeN(elem_map); - } - - if (select) { - /* was EDBM_select_flush, but it over select in edge/face mode */ - EDBM_selectmode_flush(em); - } - else { - EDBM_deselect_flush(em); - } - - DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); - } - - MEM_freeN(objects); - return OPERATOR_FINISHED; -} - -void MESH_OT_select_random(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Random"; - ot->description = "Randomly select vertices"; - ot->idname = "MESH_OT_select_random"; - - /* api callbacks */ - ot->exec = edbm_select_random_exec; - ot->poll = ED_operator_editmesh; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* props */ - WM_operator_properties_select_random(ot); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Ungrouped Operator - * \{ */ - -static bool edbm_select_ungrouped_poll(bContext *C) -{ - if (ED_operator_editmesh(C)) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - const int cd_dvert_offset = CustomData_get_offset(&em->bm->vdata, CD_MDEFORMVERT); - - const ListBase *defbase = BKE_object_defgroup_list(obedit); - if ((em->selectmode & SCE_SELECT_VERTEX) == 0) { - CTX_wm_operator_poll_msg_set(C, "Must be in vertex selection mode"); - } - else if (BLI_listbase_is_empty(defbase) || cd_dvert_offset == -1) { - CTX_wm_operator_poll_msg_set(C, "No weights/vertex groups on object"); - } - else { - return true; - } - } - return false; -} - -static int edbm_select_ungrouped_exec(bContext *C, wmOperator *op) -{ - const bool extend = RNA_boolean_get(op->ptr, "extend"); - const Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - scene, view_layer, CTX_wm_view3d(C), &objects_len); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - - const int cd_dvert_offset = CustomData_get_offset(&em->bm->vdata, CD_MDEFORMVERT); - - if (cd_dvert_offset == -1) { - continue; - } - - BMVert *eve; - BMIter iter; - - bool changed = false; - - if (!extend) { - if (em->bm->totvertsel) { - EDBM_flag_disable_all(em, BM_ELEM_SELECT); - changed = true; - } - } - - BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { - if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { - MDeformVert *dv = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); - /* no dv or dv set with no weight */ - if (ELEM(NULL, dv, dv->dw)) { - BM_vert_select_set(em->bm, eve, true); - changed = true; - } - } - } - - if (changed) { - EDBM_selectmode_flush(em); - DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); - } - } - MEM_freeN(objects); - return OPERATOR_FINISHED; -} - -void MESH_OT_select_ungrouped(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Ungrouped"; - ot->idname = "MESH_OT_select_ungrouped"; - ot->description = "Select vertices without a group"; - - /* api callbacks */ - ot->exec = edbm_select_ungrouped_exec; - ot->poll = edbm_select_ungrouped_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Axis Operator - * \{ */ - -enum { - SELECT_AXIS_POS = 0, - SELECT_AXIS_NEG = 1, - SELECT_AXIS_ALIGN = 2, -}; - -static int edbm_select_axis_exec(bContext *C, wmOperator *op) -{ - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMVert *v_act = BM_mesh_active_vert_get(em->bm); - const int orientation = RNA_enum_get(op->ptr, "orientation"); - const int axis = RNA_enum_get(op->ptr, "axis"); - const int sign = RNA_enum_get(op->ptr, "sign"); - - if (v_act == NULL) { - BKE_report( - op->reports, RPT_WARNING, "This operator requires an active vertex (last selected)"); - return OPERATOR_CANCELLED; - } - - const float limit = RNA_float_get(op->ptr, "threshold"); - - float value; - float axis_mat[3][3]; - - /* 3D view variables may be NULL, (no need to check in poll function). */ - ED_transform_calc_orientation_from_type_ex(scene, - view_layer, - CTX_wm_view3d(C), - CTX_wm_region_view3d(C), - obedit, - obedit, - orientation, - V3D_AROUND_ACTIVE, - axis_mat); - - const float *axis_vector = axis_mat[axis]; - - { - float vertex_world[3]; - mul_v3_m4v3(vertex_world, obedit->obmat, v_act->co); - value = dot_v3v3(axis_vector, vertex_world); - } - - if (sign == SELECT_AXIS_NEG) { - value += limit; - } - else if (sign == SELECT_AXIS_POS) { - value -= limit; - } - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode( - scene, view_layer, CTX_wm_view3d(C), &objects_len); - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit_iter = objects[ob_index]; - BMEditMesh *em_iter = BKE_editmesh_from_object(obedit_iter); - BMesh *bm = em_iter->bm; - - if (bm->totvert == bm->totvertsel) { - continue; - } - - BMIter iter; - BMVert *v; - bool changed = false; - - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN | BM_ELEM_SELECT)) { - float v_iter_world[3]; - mul_v3_m4v3(v_iter_world, obedit_iter->obmat, v->co); - const float value_iter = dot_v3v3(axis_vector, v_iter_world); - switch (sign) { - case SELECT_AXIS_ALIGN: - if (fabsf(value_iter - value) < limit) { - BM_vert_select_set(bm, v, true); - changed = true; - } - break; - case SELECT_AXIS_NEG: - if (value_iter < value) { - BM_vert_select_set(bm, v, true); - changed = true; - } - break; - case SELECT_AXIS_POS: - if (value_iter > value) { - BM_vert_select_set(bm, v, true); - changed = true; - } - break; - } - } - } - if (changed) { - EDBM_selectmode_flush(em_iter); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit_iter->data); - DEG_id_tag_update(obedit_iter->data, ID_RECALC_SELECT); - } - } - MEM_freeN(objects); - return OPERATOR_FINISHED; -} - -void MESH_OT_select_axis(wmOperatorType *ot) -{ - static const EnumPropertyItem axis_sign_items[] = { - {SELECT_AXIS_POS, "POS", 0, "Positive Axis", ""}, - {SELECT_AXIS_NEG, "NEG", 0, "Negative Axis", ""}, - {SELECT_AXIS_ALIGN, "ALIGN", 0, "Aligned Axis", ""}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Select Axis"; - ot->description = "Select all data in the mesh on a single axis"; - ot->idname = "MESH_OT_select_axis"; - - /* api callbacks */ - ot->exec = edbm_select_axis_exec; - ot->poll = ED_operator_editmesh; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_enum(ot->srna, - "orientation", - rna_enum_transform_orientation_items, - V3D_ORIENT_LOCAL, - "Axis Mode", - "Axis orientation"); - RNA_def_enum(ot->srna, "sign", axis_sign_items, SELECT_AXIS_POS, "Axis Sign", "Side to select"); - RNA_def_enum(ot->srna, - "axis", - rna_enum_axis_xyz_items, - 0, - "Axis", - "Select the axis to compare each vertex on"); - RNA_def_float( - ot->srna, "threshold", 0.0001f, 0.000001f, 50.0f, "Threshold", "", 0.00001f, 10.0f); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Region to Loop Operator - * \{ */ - -static int edbm_region_to_loop_exec(bContext *C, wmOperator *UNUSED(op)) -{ - const Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - scene, view_layer, CTX_wm_view3d(C), &objects_len); - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - - if (em->bm->totfacesel == 0) { - continue; - } - BMFace *f; - BMEdge *e; - BMIter iter; - - BM_mesh_elem_hflag_disable_all(em->bm, BM_EDGE, BM_ELEM_TAG, false); - - BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) { - BMLoop *l1, *l2; - BMIter liter1, liter2; - - BM_ITER_ELEM (l1, &liter1, f, BM_LOOPS_OF_FACE) { - int tot = 0, totsel = 0; - - BM_ITER_ELEM (l2, &liter2, l1->e, BM_LOOPS_OF_EDGE) { - tot++; - totsel += BM_elem_flag_test(l2->f, BM_ELEM_SELECT) != 0; - } - - if ((tot != totsel && totsel > 0) || (totsel == 1 && tot == 1)) { - BM_elem_flag_enable(l1->e, BM_ELEM_TAG); - } - } - } - - EDBM_flag_disable_all(em, BM_ELEM_SELECT); - - BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(e, BM_ELEM_TAG)) { - BM_edge_select_set(em->bm, e, true); - } - } - - /* If in face-only select mode, switch to edge select mode so that - * an edge-only selection is not inconsistent state */ - if (em->selectmode == SCE_SELECT_FACE) { - em->selectmode = SCE_SELECT_EDGE; - EDBM_selectmode_set(em); - EDBM_selectmode_to_scene(C); - } - - DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); - } - MEM_freeN(objects); - - return OPERATOR_FINISHED; -} - -void MESH_OT_region_to_loop(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Boundary Loop"; - ot->idname = "MESH_OT_region_to_loop"; - ot->description = "Select boundary edges around the selected faces"; - - /* api callbacks */ - ot->exec = edbm_region_to_loop_exec; - ot->poll = ED_operator_editmesh; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Loop to Region Operator - * \{ */ - -static int loop_find_region(BMLoop *l, int flag, GSet *visit_face_set, BMFace ***region_out) -{ - BMFace **region = NULL; - BMFace **stack = NULL; - BLI_array_declare(region); - BLI_array_declare(stack); - BMFace *f; - - BLI_array_append(stack, l->f); - BLI_gset_insert(visit_face_set, l->f); - - while (BLI_array_len(stack) > 0) { - BMIter liter1, liter2; - BMLoop *l1, *l2; - - f = BLI_array_pop(stack); - BLI_array_append(region, f); - - BM_ITER_ELEM (l1, &liter1, f, BM_LOOPS_OF_FACE) { - if (BM_elem_flag_test(l1->e, flag)) { - continue; - } - - BM_ITER_ELEM (l2, &liter2, l1->e, BM_LOOPS_OF_EDGE) { - /* avoids finding same region twice - * (otherwise) the logic works fine without */ - if (BM_elem_flag_test(l2->f, BM_ELEM_TAG)) { - continue; - } - - if (BLI_gset_add(visit_face_set, l2->f)) { - BLI_array_append(stack, l2->f); - } - } - } - } - - BLI_array_free(stack); - - *region_out = region; - return BLI_array_len(region); -} - -static int verg_radial(const void *va, const void *vb) -{ - const BMEdge *e_a = *((const BMEdge **)va); - const BMEdge *e_b = *((const BMEdge **)vb); - - const int a = BM_edge_face_count(e_a); - const int b = BM_edge_face_count(e_b); - - if (a > b) { - return -1; - } - if (a < b) { - return 1; - } - return 0; -} - -/** - * This function leaves faces tagged which are a part of the new region. - * - * \note faces already tagged are ignored, to avoid finding the same regions twice: - * important when we have regions with equal face counts, see: T40309 - */ -static int loop_find_regions(BMEditMesh *em, const bool selbigger) -{ - GSet *visit_face_set; - BMIter iter; - const int edges_len = em->bm->totedgesel; - BMEdge *e, **edges; - int count = 0, i; - - visit_face_set = BLI_gset_ptr_new_ex(__func__, edges_len); - edges = MEM_mallocN(sizeof(*edges) * edges_len, __func__); - - i = 0; - BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { - edges[i++] = e; - BM_elem_flag_enable(e, BM_ELEM_TAG); - } - else { - BM_elem_flag_disable(e, BM_ELEM_TAG); - } - } - - /* sort edges by radial cycle length */ - qsort(edges, edges_len, sizeof(*edges), verg_radial); - - for (i = 0; i < edges_len; i++) { - BMIter liter; - BMLoop *l; - BMFace **region = NULL, **region_out; - int c, tot = 0; - - e = edges[i]; - - if (!BM_elem_flag_test(e, BM_ELEM_TAG)) { - continue; - } - - BM_ITER_ELEM (l, &liter, e, BM_LOOPS_OF_EDGE) { - if (BLI_gset_haskey(visit_face_set, l->f)) { - continue; - } - - c = loop_find_region(l, BM_ELEM_SELECT, visit_face_set, ®ion_out); - - if (!region || (selbigger ? c >= tot : c < tot)) { - /* this region is the best seen so far */ - tot = c; - if (region) { - /* free the previous best */ - MEM_freeN(region); - } - /* track the current region as the new best */ - region = region_out; - } - else { - /* this region is not as good as best so far, just free it */ - MEM_freeN(region_out); - } - } - - if (region) { - int j; - - for (j = 0; j < tot; j++) { - BM_elem_flag_enable(region[j], BM_ELEM_TAG); - BM_ITER_ELEM (l, &liter, region[j], BM_LOOPS_OF_FACE) { - BM_elem_flag_disable(l->e, BM_ELEM_TAG); - } - } - - count += tot; - - MEM_freeN(region); - } - } - - MEM_freeN(edges); - BLI_gset_free(visit_face_set, NULL); - - return count; -} - -static int edbm_loop_to_region_exec(bContext *C, wmOperator *op) -{ - const bool select_bigger = RNA_boolean_get(op->ptr, "select_bigger"); - - const Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - scene, view_layer, CTX_wm_view3d(C), &objects_len); - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - - if (em->bm->totedgesel == 0) { - continue; - } - - BMIter iter; - BMFace *f; - - /* find the set of regions with smallest number of total faces */ - BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false); - const int a = loop_find_regions(em, select_bigger); - const int b = loop_find_regions(em, !select_bigger); - - BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false); - loop_find_regions(em, ((a <= b) != select_bigger) ? select_bigger : !select_bigger); - - EDBM_flag_disable_all(em, BM_ELEM_SELECT); - - BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(f, BM_ELEM_TAG) && !BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - BM_face_select_set(em->bm, f, true); - } - } - - EDBM_selectmode_flush(em); - - DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); - } - MEM_freeN(objects); - - return OPERATOR_FINISHED; -} - -void MESH_OT_loop_to_region(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Loop Inner-Region"; - ot->idname = "MESH_OT_loop_to_region"; - ot->description = "Select region of faces inside of a selected loop of edges"; - - /* api callbacks */ - ot->exec = edbm_loop_to_region_exec; - ot->poll = ED_operator_editmesh; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_boolean(ot->srna, - "select_bigger", - 0, - "Select Bigger", - "Select bigger regions instead of smaller ones"); -} - -/** \} */ -- cgit v1.2.3