implemented select (x-)mirrored. I used a topological approach, which will be later reused in x-mirror editing, weight painting, etc. also ported select randon, select linked flat faces, and select sharp (as in, surroundined by non-flat faces, not sharp as in the sharp flag) edges, and select non-manifold. need to merge in recent trunk changes next.
This commit is contained in:
parent
08351bf3d6
commit
ea540496c8
|
@ -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.");
|
||||
}
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue