diff --git a/source/blender/editors/mesh/bmesh_select.c b/source/blender/editors/mesh/bmesh_select.c index aa6a700eb6f..85f7ee1aabc 100644 --- a/source/blender/editors/mesh/bmesh_select.c +++ b/source/blender/editors/mesh/bmesh_select.c @@ -2073,3 +2073,251 @@ int EM_view3d_poll(bContext *C) return 1; return 0; } + + +static int 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 + */ + Object *obedit= CTX_data_edit_object(C); + BMEditMesh *em= ((Mesh *)obedit->data)->edit_btmesh; + BMIter iter; + BMEdge *e; + BLI_array_declare(stack); + BMLoop *l1, *l2; + float sharp = RNA_float_get(op->ptr, "sharpness"), angle; + + sharp = (sharp * M_PI) / 180.0; + + BM_ITER(e, &iter, em->bm, BM_EDGES_OF_MESH, NULL) { + if (BM_TestHFlag(e, BM_HIDDEN) || !e->loop) + continue; + + l1 = e->loop; + l2 = l1->radial.next->data; + + if (l1 == l2) + continue; + + /* edge has exactly two neighboring faces, check angle */ + angle = saacos(l1->f->no[0]*l2->f->no[0]+l1->f->no[1]*l2->f->no[1]+l1->f->no[2]*l2->f->no[2]); + + if (fabs(angle) < sharp) { + BM_Select(em->bm, e, 1); + } + + } + + WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); + + return OPERATOR_FINISHED; +} + +void MESH_OT_edges_select_sharp(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Select Sharp Edges"; + ot->description= "Marked selected edges as sharp."; + ot->idname= "MESH_OT_edges_select_sharp"; + + /* api callbacks */ + ot->exec= select_sharp_edges_exec; + ot->poll= ED_operator_editmesh; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* props */ + RNA_def_float(ot->srna, "sharpness", 1.0f, 0.01f, FLT_MAX, "sharpness", "", 1.0f, 180.0f); +} + +static int select_linked_flat_faces_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + BMEditMesh *em= ((Mesh *)obedit->data)->edit_btmesh; + BMIter iter, liter, liter2; + BMFace *f, **stack = NULL; + BLI_array_declare(stack); + BMLoop *l, *l2; + float sharp = RNA_float_get(op->ptr, "sharpness"); + int i; + + sharp = (sharp * M_PI) / 180.0; + + BM_ITER(f, &iter, em->bm, BM_FACES_OF_MESH, NULL) { + BMINDEX_SET(f, 0); + } + + BM_ITER(f, &iter, em->bm, BM_FACES_OF_MESH, NULL) { + if (BM_TestHFlag(f, BM_HIDDEN) || !BM_TestHFlag(f, BM_SELECT) || BMINDEX_GET(f)) + continue; + + BLI_array_empty(stack); + i = 1; + + BLI_array_growone(stack); + stack[i-1] = f; + + while (i) { + f = stack[i-1]; + i--; + + BM_Select(em->bm, f, 1); + + BMINDEX_SET(f, 1); + + BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, f) { + BM_ITER(l2, &liter2, em->bm, BM_LOOPS_OF_LOOP, l) { + float angle; + + if (BMINDEX_GET(l2->f) || BM_TestHFlag(l2->f, BM_HIDDEN)) + continue; + + /* edge has exactly two neighboring faces, check angle */ + angle = saacos(f->no[0]*l2->f->no[0]+f->no[1]*l2->f->no[1]+f->no[2]*l2->f->no[2]); + + /* invalidate: edge too sharp */ + if (fabs(angle) < sharp) { + BLI_array_growone(stack); + stack[i] = l2->f; + i++; + } + } + } + } + } + + BLI_array_free(stack); + + WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); + + return OPERATOR_FINISHED; +} + +void MESH_OT_faces_select_linked_flat(wmOperatorType *ot) +{ + /* 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= select_linked_flat_faces_exec; + ot->poll= ED_operator_editmesh; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* props */ + RNA_def_float(ot->srna, "sharpness", 1.0f, 0.01f, FLT_MAX, "sharpness", "", 1.0f, 180.0f); +} + +static int select_non_manifold_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + BMEditMesh *em= ((Mesh *)obedit->data)->edit_btmesh; + BMVert *v; + BMEdge *e; + BMIter iter; + + /* Selects isolated verts, and edges that do not have 2 neighboring + * faces + */ + + if(em->selectmode==SCE_SELECT_FACE) { + BKE_report(op->reports, RPT_ERROR, "Doesn't work in face selection mode"); + return OPERATOR_CANCELLED; + } + + BM_ITER(v, &iter, em->bm, BM_VERTS_OF_MESH, NULL) { + if (!BM_TestHFlag(em->bm, BM_HIDDEN) && BM_Nonmanifold_Vert(em->bm, v)) + BM_Select(em->bm, v, 1); + } + + BM_ITER(e, &iter, em->bm, BM_EDGES_OF_MESH, NULL) { + if (!BM_TestHFlag(em->bm, BM_HIDDEN) && BM_Nonmanifold_Edge(em->bm, e)) + BM_Select(em->bm, e, 1); + } + + WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); + + 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= select_non_manifold_exec; + ot->poll= ED_operator_editmesh; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +static int mesh_select_random_exec(bContext *C, wmOperator *op) +{ + Object *obedit= CTX_data_edit_object(C); + BMEditMesh *em= ((Mesh *)obedit->data)->edit_btmesh; + BMVert *eve; + BMEdge *eed; + BMFace *efa; + BMIter iter; + float randfac = RNA_float_get(op->ptr, "percent")/100.0f; + + BLI_srand( BLI_rand() ); /* random seed */ + + if(!RNA_boolean_get(op->ptr, "extend")) + EDBM_clear_flag_all(em, BM_SELECT); + + if(em->selectmode & SCE_SELECT_VERTEX) { + BM_ITER(eve, &iter, em->bm, BM_VERTS_OF_MESH, NULL) { + if (!BM_TestHFlag(eve, BM_HIDDEN) && BLI_frand() < randfac) + BM_Select(em->bm, eve, 1); + } + EDBM_selectmode_flush(em); + } + else if(em->selectmode & SCE_SELECT_EDGE) { + BM_ITER(eed, &iter, em->bm, BM_EDGES_OF_MESH, NULL) { + if (!BM_TestHFlag(eed, BM_HIDDEN) && BLI_frand() < randfac) + BM_Select(em->bm, eed, 1); + } + EDBM_selectmode_flush(em); + } + else { + BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) { + if (!BM_TestHFlag(efa, BM_HIDDEN) && BLI_frand() < randfac) + BM_Select(em->bm, efa, 1); + } + EDBM_selectmode_flush(em); + } + + WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); + + 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= mesh_select_random_exec; + ot->poll= ED_operator_editmesh; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* props */ + RNA_def_float_percentage(ot->srna, "percent", 50.f, 0.0f, 100.0f, "Percent", "Percentage of elements to select randomly.", 0.f, 100.0f); + RNA_def_boolean(ot->srna, "extend", FALSE, "Extend Selection", "Extend selection instead of deselecting everything first."); +} diff --git a/source/blender/editors/mesh/bmesh_tools.c b/source/blender/editors/mesh/bmesh_tools.c index c5d1ce9b2f5..87eb5fb6da7 100644 --- a/source/blender/editors/mesh/bmesh_tools.c +++ b/source/blender/editors/mesh/bmesh_tools.c @@ -4645,18 +4645,41 @@ void MESH_OT_select_by_number_vertices(wmOperatorType *ot) } +#define MIRROR_THRESH 1.0f + int select_mirror_exec(bContext *C, wmOperator *op) { -#if 0 + Object *obedit= CTX_data_edit_object(C); - EditMesh *em= BKE_mesh_get_editmesh(((Mesh *)obedit->data)); - + BMEditMesh *em= ((Mesh *)obedit->data)->edit_btmesh; + BMBVHTree *tree = BMBVH_NewBVH(em); + BMVert *v1, *v2; + BMIter iter; int extend= RNA_boolean_get(op->ptr, "extend"); + float mirror_co[3]; - EM_select_mirrored(obedit, em, extend); + BM_ITER(v1, &iter, em->bm, BM_VERTS_OF_MESH, NULL) { + if (!BM_TestHFlag(v1, BM_SELECT) || BM_TestHFlag(v1, BM_HIDDEN)) + BMINDEX_SET(v1, 0); + else BMINDEX_SET(v1, 1); + } + + if (!extend) + EDBM_clear_flag_all(em, BM_SELECT); + + BM_ITER(v1, &iter, em->bm, BM_VERTS_OF_MESH, NULL) { + if (!BMINDEX_GET(v1) || BM_TestHFlag(v1, BM_HIDDEN)) + continue; + + VECCOPY(mirror_co, v1->co); + mirror_co[0] *= -1.0f; + + v2 = BMBVH_FindClosestVertTopo(tree, mirror_co, MIRROR_THRESH, v1); + if (v2 && !BM_TestHFlag(v2, BM_HIDDEN)) + BM_Select(em->bm, v2, 1); + } WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); -#endif return OPERATOR_FINISHED; } @@ -4677,129 +4700,3 @@ void MESH_OT_select_mirror(wmOperatorType *ot) /* props */ RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the existing selection"); } - -static int 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 - */ -} - -void MESH_OT_edges_select_sharp(wmOperatorType *ot) -{ - /* identifiers */ - ot->name= "Select Sharp Edges"; - ot->description= "Marked selected edges as sharp."; - ot->idname= "MESH_OT_edges_select_sharp"; - - /* api callbacks */ - ot->exec= select_sharp_edges_exec; - ot->poll= ED_operator_editmesh; - - /* flags */ - ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; - - /* props */ - RNA_def_float(ot->srna, "sharpness", 0.01f, 0.0f, FLT_MAX, "sharpness", "", 0.0f, 180.0f); -} - -static int select_linked_flat_faces_exec(bContext *C, wmOperator *op) -{ -#if 0 - Object *obedit= CTX_data_edit_object(C); - EditMesh *em= BKE_mesh_get_editmesh(((Mesh *)obedit->data)); - - select_linked_flat_faces(em, op, RNA_float_get(op->ptr, "sharpness")); - - WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); - - BKE_mesh_end_editmesh(obedit->data, em); - return OPERATOR_FINISHED; -#endif -} - -void MESH_OT_faces_select_linked_flat(wmOperatorType *ot) -{ - /* 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= select_linked_flat_faces_exec; - ot->poll= ED_operator_editmesh; - - /* flags */ - ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; - - /* props */ - RNA_def_float(ot->srna, "sharpness", 0.0f, 0.0f, FLT_MAX, "sharpness", "", 0.0f, 180.0f); -} - -static int select_non_manifold_exec(bContext *C, wmOperator *op) -{ -#if 0 - Object *obedit= CTX_data_edit_object(C); - EditMesh *em= BKE_mesh_get_editmesh(((Mesh *)obedit->data)); - - select_non_manifold(em, op); - - WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); - - BKE_mesh_end_editmesh(obedit->data, em); - return OPERATOR_FINISHED; -#endif -} - -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= select_non_manifold_exec; - ot->poll= ED_operator_editmesh; - - /* flags */ - ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; -} - -static int mesh_select_random_exec(bContext *C, wmOperator *op) -{ -#if 0 - Object *obedit= CTX_data_edit_object(C); - EditMesh *em= BKE_mesh_get_editmesh(((Mesh *)obedit->data)); - - if(!RNA_boolean_get(op->ptr, "extend")) - EM_deselect_all(em); - - selectrandom_mesh(em, RNA_float_get(op->ptr, "percent")/100.0f); - - WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); - - BKE_mesh_end_editmesh(obedit->data, em); -#endif - 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= mesh_select_random_exec; - ot->poll= ED_operator_editmesh; - - /* flags */ - ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; - - /* props */ - RNA_def_float_percentage(ot->srna, "percent", 50.f, 0.0f, 100.0f, "Percent", "Percentage of elements to select randomly.", 0.f, 100.0f); - RNA_def_boolean(ot->srna, "extend", FALSE, "Extend Selection", "Extend selection instead of deselecting everything first."); -} diff --git a/source/blender/editors/mesh/editbmesh_bvh.c b/source/blender/editors/mesh/editbmesh_bvh.c index 6ce2f04fc59..d77ac5ed0bc 100644 --- a/source/blender/editors/mesh/editbmesh_bvh.c +++ b/source/blender/editors/mesh/editbmesh_bvh.c @@ -98,6 +98,14 @@ typedef struct BMBVHTree { BMesh *bm; BVHTree *tree; float epsilon; + float maxdist; //for nearest point search + + /*stuff for topological vert search*/ + BMVert *v, *curv; + GHash *gh; + float curw, curd; + float co[3]; + int curtag; } BMBVHTree; BMBVHTree *BMBVH_NewBVH(BMEditMesh *em) @@ -219,6 +227,369 @@ BMFace *BMBVH_RayCast(BMBVHTree *tree, float *co, float *dir, float *hitout) return NULL; } + +static void vertsearchcallback(void *userdata, int index, const float *co, BVHTreeNearest *hit) +{ + BMBVHTree *tree = userdata; + BMLoop **ls = tree->em->looptris[index]; + float dist, maxdist, v[3]; + int i; + + maxdist = tree->maxdist; + + for (i=0; i<3; i++) { + sub_v3_v3v3(v, hit->co, ls[i]->v->co); + + dist = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); + if (dist < hit->dist && dist < maxdist) { + VECCOPY(hit->co, ls[i]->v->co); + VECCOPY(hit->no, ls[i]->v->no); + hit->dist = dist; + } + } +} + +BMVert *BMBVH_FindClosestVert(BMBVHTree *tree, float *co, float maxdist) +{ + BVHTreeNearest hit; + + VECCOPY(hit.co, co); + hit.dist = maxdist*5; + hit.index = -1; + + tree->maxdist = maxdist; + + BLI_bvhtree_find_nearest(tree->tree, co, &hit, vertsearchcallback, tree); + if (hit.dist != FLT_MAX && hit.index != -1) { + BMLoop **ls = tree->em->looptris[hit.index]; + float dist, curdist = tree->maxdist, v[3]; + int cur=0, i; + + maxdist = tree->maxdist; + + for (i=0; i<3; i++) { + sub_v3_v3v3(v, hit.co, ls[i]->v->co); + + dist = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); + if (dist < curdist) { + cur = i; + curdist = dist; + } + } + + return ls[i]->v; + } + + return NULL; +} + +typedef struct walklist { + BMVert *v; + int valence; + int depth; + float w, r; + int totwalked; + + /*state data*/ + BMVert *lastv; + BMLoop *curl, *firstl; + BMEdge *cure; +} walklist; + + +static short winding(float *v1, float *v2, float *v3) +/* is v3 to the right of v1-v2 ? With exception: v3==v1 || v3==v2 */ +{ + double inp; + + //inp= (v2[cox]-v1[cox])*(v1[coy]-v3[coy]) +(v1[coy]-v2[coy])*(v1[cox]-v3[cox]); + inp= (v2[0]-v1[0])*(v1[1]-v3[1]) +(v1[1]-v2[1])*(v1[0]-v3[0]); + + if(inp<0.0) return 0; + else if(inp==0) { + if(v1[0]==v3[0] && v1[1]==v3[1]) return 0; + if(v2[0]==v3[0] && v2[1]==v3[1]) return 0; + } + return 1; +} + +static float topo_compare(BMesh *bm, BMVert *v1, BMVert *v2, int tag) +{ + BMIter iter1, iter2; + BMEdge *e1, *e2, *cure1 = NULL, *cure2 = NULL; + BMLoop *l1, *l2; + BMVert *lastv1, *lastv2; + GHash *gh; + walklist *stack1=NULL, *stack2=NULL; + BLI_array_declare(stack1); + BLI_array_declare(stack2); + float vec1[3], vec2[3], minangle=FLT_MAX, w; + int lvl=1; + static int maxlevel = 8; + + /*ok. see how similar v is to v2, based on topological similaritys in the local + topological neighborhood*/ + + /*step 1: find two edges, one that contains v and one that contains v2, with the + smallest angle between the two edges*/ + + BM_ITER(e1, &iter1, bm, BM_EDGES_OF_VERT, v1) { + BM_ITER(e2, &iter2, bm, BM_EDGES_OF_VERT, v2) { + float angle; + + if (e1->v1 == e2->v1 || e1->v2 == e2->v2 || e1->v1 == e2->v2 || e1->v2 == e2->v1) + continue; + + sub_v3_v3v3(vec1, BM_OtherEdgeVert(e1, v1)->co, v1->co); + sub_v3_v3v3(vec2, BM_OtherEdgeVert(e2, v2)->co, v2->co); + + angle = fabs(angle_v3v3(vec1, vec2)); + + if (angle < minangle) { + minangle = angle; + cure1 = e1; + cure2 = e2; + } + } + } + + if (!cure1 || !cure1->loop || !cure2->loop) { + /*just return 1.0 in this case*/ + return 1.0f; + } + + /*assumtions + + we assume a 2-manifold mesh here. if at any time this isn't the case, + e.g. a hole or an edge with more then 2 faces around it, we um ignore + that edge I guess, and try to make the algorithm go around as necassary.*/ + + l1 = cure1->loop; + l2 = cure2->loop; + + lastv1 = l1->v == v1 ? ((BMLoop*)l1->head.next)->v : ((BMLoop*)l1->head.prev)->v; + lastv2 = l2->v == v2 ? ((BMLoop*)l2->head.next)->v : ((BMLoop*)l2->head.prev)->v; + + /*we can only provide meaningful comparisons if v1 and v2 have the same valence*/ + if (BM_Vert_EdgeCount(v1) != BM_Vert_EdgeCount(v2)) + return 1.0f; /*full mismatch*/ + + gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + +#define SPUSH(s, d, vt, lv, e)\ + if (BLI_array_count(s) <= lvl) BLI_array_growone(s);\ + memset((s+lvl), 0, sizeof(*s));\ + s[lvl].depth = d;\ + s[lvl].v = vt;\ + s[lvl].cure = e;\ + s[lvl].lastv = lv;\ + s[lvl].valence = BM_Vert_EdgeCount(vt);\ + + lvl = 0; + + SPUSH(stack1, 0, v1, lastv1, cure1); + SPUSH(stack2, 0, v2, lastv2, cure2); + + BLI_srand( BLI_rand() ); /* random seed */ + + lvl = 1; + while (lvl) { + int term = 0; + walklist *s1 = stack1 + lvl - 1, *s2 = stack2 + lvl - 1; + + /*pop from the stack*/ + lvl--; + + if (s1->curl && s1->curl->e == s1->cure) + term = 1; + if (s2->curl && s2->curl->e == s2->cure) + term = 1; + + /*find next case to do*/ + if (!s1->curl) + s1->curl = s1->cure->loop; + if (!s2->curl) { + int wind1, wind2; + + s2->curl = s2->cure->loop; + + /*find which of two possible faces to use*/ + wind1 = winding(s1->v->co, s1->lastv->co, + s1->v == s1->curl->v ? ((BMLoop*)s1->curl->head.prev->prev)->v->co : ((BMLoop*)s1->curl->head.next->next)->v->co); + + wind2 = winding(s2->v->co, s2->lastv->co, + s2->v == s2->curl->v ? ((BMLoop*)s2->curl->head.prev->prev)->v->co : ((BMLoop*)s2->curl->head.next->next)->v->co); + + if (wind1 == wind2) + s2->curl = s2->curl->radial.next->data; + } + + /*handle termination cases of having already looped through all child + nodes, or the valence mismatching between v1 and v2, or we hit max + recursion depth*/ + term |= s1->valence != s2->valence || lvl+1 > maxlevel; + term |= s1->curl->radial.next->data == (BMLoop*)l1; + term |= s2->curl->radial.next->data == (BMLoop*)l2; + + if (!term) { + lastv1 = s1->v; + lastv2 = s2->v; + v1 = BM_OtherEdgeVert(s1->curl->e, lastv1); + v2 = BM_OtherEdgeVert(s2->curl->e, lastv2); + + e1 = s1->curl->e; + e2 = s2->curl->e; + + if (!BLI_ghash_haskey(gh, v1) && !BLI_ghash_haskey(gh, v2)) { + /*repush the current stack item*/ + lvl++; + + if (maxlevel % 2 == 0) { + BLI_ghash_insert(gh, v1, NULL); + BLI_ghash_insert(gh, v2, NULL); + } + + /*now push the child node*/ + SPUSH(stack1, lvl, v1, lastv1, e1); + SPUSH(stack2, lvl, v2, lastv2, e2); + + lvl++; + + s1 = stack1 + lvl - 2; + s2 = stack2 + lvl - 2; + } + + s1->curl = s1->curl->v == s1->v ? (BMLoop*) s1->curl->head.prev : (BMLoop*) s1->curl->head.next; + s2->curl = s2->curl->v == s2->v ? (BMLoop*) s2->curl->head.prev : (BMLoop*) s2->curl->head.next; + + s1->curl = (BMLoop*) s1->curl->radial.next->data; + s2->curl = (BMLoop*) s2->curl->radial.next->data; + } + +#define WADD(stack, s)\ + if (lvl) {/*silly attempt to make this non-commutative: randomize\ + how much this particular weight adds to the total*/\ + stack[lvl-1].r += r;\ + s->w *= r;\ + stack[lvl-1].totwalked++;\ + stack[lvl-1].w += s->w;\ + } + + /*if no next case to do, update parent weight*/ + if (term) { + float r = 0.8f + BLI_frand()*0.2f - FLT_EPSILON; + + if (s1->totwalked) { + s1->w /= s1->r; + } else + s1->w = s1->valence == s2->valence ? 1.0f : 0.0f; + + WADD(stack1, s1); + + if (s2->totwalked) { + s2->w /= s2->r; + } else + s2->w = s1->valence == s2->valence ? 1.0f : 0.0f; + + WADD(stack2, s2); + + /*apply additional penalty to weight mismatch*/ + if (s2->w != s1->w) + s2->w *= 0.8f; + } + } + + w = (stack1[0].w + stack2[0].w)*0.5f; + + BLI_array_free(stack1); + BLI_array_free(stack2); + + BLI_ghash_free(gh, NULL, NULL); + + return 1.0f - w; +} + +static void vertsearchcallback_topo(void *userdata, int index, const float *co, BVHTreeNearest *hit) +{ + BMBVHTree *tree = userdata; + BMLoop **ls = tree->em->looptris[index]; + int i; + float dist, maxdist, vec[3], w; + + maxdist = tree->maxdist; + + for (i=0; i<3; i++) { + float dis; + + if (BLI_ghash_haskey(tree->gh, ls[i]->v)) + continue; + + sub_v3_v3v3(vec, tree->co, ls[i]->v->co); + dis = dot_v3v3(vec, vec); + + w = topo_compare(tree->em->bm, tree->v, ls[i]->v, tree->curtag++); + + if (w < tree->curw-FLT_EPSILON*4) { + tree->curw = w; + tree->curv = ls[i]->v; + + sub_v3_v3v3(vec, tree->co, ls[i]->v->co); + tree->curd = dot_v3v3(vec, vec); + + /*we deliberately check for equality using (smallest possible float)*4 + comparison factor, to always prefer distance in cases of verts really + close to each other*/ + } else if (fabs(tree->curw - w) < FLT_EPSILON*4) { + /*if w is equal to hitex->curw, sort by distance*/ + sub_v3_v3v3(vec, tree->co, ls[i]->v->co); + dis = dot_v3v3(vec, vec); + + if (dis < tree->curd) { + tree->curd = dis; + tree->curv = ls[i]->v; + } + } + + BLI_ghash_insert(tree->gh, ls[i]->v, NULL); + } +} + +BMVert *BMBVH_FindClosestVertTopo(BMBVHTree *tree, float *co, float maxdist, BMVert *sourcev) +{ + BVHTreeNearest hit; + BMVert *v; + BMIter iter; + + memset(&hit, 0, sizeof(hit)); + + VECCOPY(hit.co, co); + VECCOPY(tree->co, co); + hit.index = -1; + hit.dist = 10.0f; + + tree->curw = FLT_MAX; + tree->curd = FLT_MAX; + tree->curv = NULL; + tree->curtag = 1; + + BM_ITER(v, &iter, tree->bm, BM_VERTS_OF_MESH, NULL) { + BMINDEX_SET(v, 0); + } + + tree->gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + + tree->maxdist = maxdist; + tree->v = sourcev; + + BLI_bvhtree_find_nearest(tree->tree, co, &hit, vertsearchcallback_topo, tree); + + BLI_ghash_free(tree->gh, NULL, NULL); + tree->gh = NULL; + + return tree->curv; +} + + #if 0 //BMESH_TODO: not implemented yet int BMBVH_VertVisible(BMBVHTree *tree, BMEdge *e, RegionView3D *r3d) { diff --git a/source/blender/editors/mesh/editbmesh_bvh.h b/source/blender/editors/mesh/editbmesh_bvh.h index 5bc403828a9..f188d616978 100644 --- a/source/blender/editors/mesh/editbmesh_bvh.h +++ b/source/blender/editors/mesh/editbmesh_bvh.h @@ -13,5 +13,11 @@ struct BMBVHTree *BMBVH_NewBVH(struct BMEditMesh *em); void BMBVH_FreeBVH(struct BMBVHTree *tree); struct BMFace *BMBVH_RayCast(struct BMBVHTree *tree, float *co, float *dir, float *hitout); + int BMBVH_EdgeVisible(struct BMBVHTree *tree, struct BMEdge *e, struct RegionView3D *r3d, struct Object *obedit); + +/*find a vert closest to co in a sphere of radius maxdist*/ +struct BMVert *BMBVH_FindClosestVert(struct BMBVHTree *tree, float *co, float maxdist); +struct BMVert *BMBVH_FindClosestVertTopo(struct BMBVHTree *tree, float *co, + float maxdist, struct BMVert *sourcev);