Index: source/blender/bmesh/bmesh_operators.h =================================================================== --- source/blender/bmesh/bmesh_operators.h (revision 22616) +++ source/blender/bmesh/bmesh_operators.h (working copy) @@ -22,6 +22,15 @@ SUBD_STRAIGHT_CUT, }; +enum { + SIMFACE_MATERIAL = 201, + SIMFACE_IMAGE, + SIMFACE_AREA, + SIMFACE_PERIMETER, + SIMFACE_NORMAL, + SIMFACE_COPLANAR, +}; + extern BMOpDefine *opdefines[]; extern int bmesh_total_ops; Index: source/blender/bmesh/intern/bmesh_opdefines.c =================================================================== --- source/blender/bmesh/intern/bmesh_opdefines.c (revision 22616) +++ source/blender/bmesh/intern/bmesh_opdefines.c (working copy) @@ -613,6 +613,22 @@ 0 }; +/* + Similar faces select + + Select similar faces (area/material/perimeter....). +*/ +BMOpDefine def_similarfaces = { + "similarfaces", + {{BMOP_OPSLOT_ELEMENT_BUF, "faces"}, /* input faces */ + {BMOP_OPSLOT_ELEMENT_BUF, "facesout"}, /* output faces */ + {BMOP_OPSLOT_INT, "type"}, /* type of selection */ + {BMOP_OPSLOT_FLT, "thresh"}, /* threshold of selection */ + {0} /*null-terminating sentinel*/}, + bmesh_similarfaces_exec, + 0 +}; + BMOpDefine *opdefines[] = { &def_splitop, &def_dupeop, @@ -653,6 +669,7 @@ &def_collapse_uvs, &def_pointmerge, &def_collapse, + &def_similarfaces, }; int bmesh_total_ops = (sizeof(opdefines) / sizeof(void*)); Index: source/blender/bmesh/intern/bmesh_operators_private.h =================================================================== --- source/blender/bmesh/intern/bmesh_operators_private.h (revision 22616) +++ source/blender/bmesh/intern/bmesh_operators_private.h (working copy) @@ -46,5 +46,6 @@ void bmesh_collapsecon_exec(BMesh *bm, BMOperator *op); void bmesh_pointmerge_exec(BMesh *bm, BMOperator *op); void bmesh_collapse_exec(BMesh *bm, BMOperator *op); +void bmesh_similarfaces_exec(BMesh *bm, BMOperator *op); #endif Index: source/blender/bmesh/operators/utils.c =================================================================== --- source/blender/bmesh/operators/utils.c (revision 22616) +++ source/blender/bmesh/operators/utils.c (working copy) @@ -383,3 +383,265 @@ V_FREE(cos); } + +/* +** compute the centroid of an ngon +** +** NOTE: This should probably go to bmesh_polygon.c and replace the function that compute its center +** basing on bounding box +*/ +static void ngon_center(float *v, BMesh *bm, BMFace *f) +{ + BMIter liter; + BMLoop *l; + v[0] = v[1] = v[2] = 0; + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + VecAddf(v, v, l->v->co); + } + + if( f->len ) + { + v[0] /= f->len; + v[1] /= f->len; + v[2] /= f->len; + } +} + +/* +** compute the perimeter of an ngon +** +** NOTE: This should probably go to bmesh_polygon.c +*/ +static float ngon_perimeter(BMesh *bm, BMFace *f) +{ + BMIter liter; + BMLoop *l; + int num_verts = 0; + float v[3], sv[3]; + float perimeter = 0.0f; + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + if( num_verts == 0 ) { + sv[0] = v[0] = l->v->co[0]; + sv[1] = v[1] = l->v->co[1]; + sv[2] = v[2] = l->v->co[2]; + num_verts++; + } else { + perimeter += VecLenf(v, l->v->co); + v[0] = l->v->co[0]; + v[1] = l->v->co[1]; + v[2] = l->v->co[2]; + num_verts++; + } + } + + perimeter += VecLenf(v, sv); + + return perimeter; +} + +/* +** compute the fake surface of an ngon +** This is done by decomposing the ngon into triangles who share the centroid of the ngon +** while this method is far from being exact, it should garantee an invariance. +** +** NOTE: This should probably go to bmesh_polygon.c +*/ +static float ngon_fake_area(BMesh *bm, BMFace *f) +{ + BMIter liter; + BMLoop *l; + int num_verts = 0; + float v[3], sv[3], c[3]; + float area = 0.0f; + + ngon_center(c, bm, f); + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + if( num_verts == 0 ) { + sv[0] = v[0] = l->v->co[0]; + sv[1] = v[1] = l->v->co[1]; + sv[2] = v[2] = l->v->co[2]; + num_verts++; + } else { + area += AreaT3Dfl(v, c, l->v->co); + v[0] = l->v->co[0]; + v[1] = l->v->co[1]; + v[2] = l->v->co[2]; + num_verts++; + } + } + + area += AreaT3Dfl(v, c, sv); + + return area; +} + +/* +** extra face data (computed data) +*/ +typedef struct tmp_face_ext { + BMFace *f; /* the face */ + float c[3]; /* center */ + union { + float area; /* area */ + float perim; /* perimeter */ + float d; /* 4th component of plane (the first three being the normal) */ + struct Image *t; /* image pointer */ + }; +} tmp_face_ext; + +/* +** Select similar faces, the choices are in the enum in source/blender/bmesh/bmesh_operators.h +** We select either similar faces based on material, image, area, perimeter, normal, or the coplanar faces +*/ +void bmesh_similarfaces_exec(BMesh *bm, BMOperator *op) +{ + BMIter fm_iter; + BMFace *fs, *fm; + BMOIter fs_iter; + int num_tex, num_sels = 0, num_total = 0, i = 0, idx = 0; + float angle = 0.0f; + tmp_face_ext *f_ext = NULL; + int *indices = NULL; + float t_no[3]; /* temporary normal */ + + int type = BMO_Get_Int(op, "type"); + float thresh = BMO_Get_Float(op, "thresh"); + + num_total = BM_Count_Element(bm, BM_FACE); + + /* + ** The first thing to do is to iterate through all the the selected items and mark them since + ** they will be in the selection anyway. + ** This will increase performance, (especially when the number of originaly selected faces is high) + ** so the overall complexity will be less than $O(mn)$ where is the total number of selected faces, + ** and n is the total number of faces + */ + BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) { + if (!BMO_TestFlag(bm, fs, FACE_MARK)) { /* is this really needed ? */ + BMO_SetFlag(bm, fs, FACE_MARK); + num_sels++; + } + } + + /* allocate memory for the selected faces indices and for all temporary faces */ + indices = (int*)malloc(sizeof(int) * num_sels); + f_ext = (tmp_face_ext*)malloc(sizeof(tmp_face_ext) * num_total); + + /* loop through all the faces and fill the faces/indices structure */ + BM_ITER(fm, &fm_iter, bm, BM_FACES_OF_MESH, NULL) { + f_ext[i].f = fm; + if (BMO_TestFlag(bm, fm, FACE_MARK)) { + indices[idx] = i; + idx++; + } + i++; + } + + /* + ** Save us some computation burden: In case of perimeter/area/coplanar selection we compute + ** only once. + */ + if( type == SIMFACE_PERIMETER || type == SIMFACE_AREA || type == SIMFACE_COPLANAR || type == SIMFACE_IMAGE ) { + for( i = 0; i < num_total; i++ ) { + switch( type ) { + case SIMFACE_PERIMETER: + /* set the perimeter */ + f_ext[i].perim = ngon_perimeter(bm, f_ext[i].f); + break; + + case SIMFACE_COPLANAR: + /* compute the center of the polygon */ + ngon_center(f_ext[i].c, bm, f_ext[i].f); + + /* normalize the polygon normal */ + VecCopyf(t_no, f_ext[i].f->no); + Normalize(t_no); + + /* compute the plane distance */ + f_ext[i].d = Inpf(t_no, f_ext[i].c); + break; + + case SIMFACE_AREA: + f_ext[i].area = ngon_fake_area(bm, f_ext[i].f); + break; + + case SIMFACE_IMAGE: + f_ext[i].t = NULL; + if( CustomData_has_layer(&(bm->pdata), CD_MTEXPOLY) ) { + MTexPoly *mtpoly = CustomData_bmesh_get(&(bm->pdata), f_ext[i].f->head.data, CD_MTEXPOLY); + f_ext[i].t = mtpoly->tpage; + } + break; + } + } + } + + /* now select the rest (if any) */ + //BM_ITER(fm, &fm_iter, bm, BM_FACES_OF_MESH, NULL) { + for( i = 0; i < num_total; i++ ) { + fm = f_ext[i].f; + if (!BMO_TestFlag(bm, fm, FACE_MARK)) { + //BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) { + int cont = 1; + for( idx = 0; idx < num_sels && cont == 1; idx++ ) { + fs = f_ext[indices[idx]].f; + switch( type ) { + case SIMFACE_MATERIAL: + if( fm->mat_nr == fs->mat_nr ) { + BMO_SetFlag(bm, fm, FACE_MARK); + cont = 0; + } + break; + + case SIMFACE_IMAGE: + if( f_ext[i].t == f_ext[indices[idx]].t ) { + BMO_SetFlag(bm, fm, FACE_MARK); + cont = 0; + } + break; + + case SIMFACE_NORMAL: + angle = VecAngle2(fs->no, fm->no); /* if the angle between the normals -> 0 */ + if( angle / 180.0 <= thresh ) { + BMO_SetFlag(bm, fm, FACE_MARK); + cont = 0; + } + break; + + case SIMFACE_COPLANAR: + angle = VecAngle2(fs->no, fm->no); /* angle -> 0 */ + if( angle / 180.0 <= thresh ) { /* and dot product difference -> 0 */ + if( fabs(f_ext[i].d - f_ext[indices[idx]].d) <= thresh ) { + BMO_SetFlag(bm, fm, FACE_MARK); + cont = 0; + } + } + break; + + case SIMFACE_AREA: + if( fabs(f_ext[i].area - f_ext[indices[idx]].area) <= thresh ) { + BMO_SetFlag(bm, fm, FACE_MARK); + cont = 0; + } + break; + + case SIMFACE_PERIMETER: + if( fabs(f_ext[i].perim - f_ext[indices[idx]].perim) <= thresh ) { + BMO_SetFlag(bm, fm, FACE_MARK); + cont = 0; + } + break; + } + } + } + } + + free(f_ext); + free(indices); + + /* transfer all marked faces to the output slot */ + BMO_Flag_To_Slot(bm, op, "facesout", FACE_MARK, BM_FACE); +} Index: source/blender/editors/mesh/bmesh_select.c =================================================================== --- source/blender/editors/mesh/bmesh_select.c (revision 22616) +++ source/blender/editors/mesh/bmesh_select.c (working copy) @@ -649,13 +649,6 @@ /* FACES GROUP */ -#define SIMFACE_MATERIAL 201 -#define SIMFACE_IMAGE 202 -#define SIMFACE_AREA 203 -#define SIMFACE_PERIMETER 204 -#define SIMFACE_NORMAL 205 -#define SIMFACE_COPLANAR 206 - static EnumPropertyItem prop_simface_types[] = { {SIMFACE_MATERIAL, "MATERIAL", 0, "Material", ""}, {SIMFACE_IMAGE, "IMAGE", 0, "Image", ""}, @@ -801,25 +794,38 @@ static int similar_face_select_exec(bContext *C, wmOperator *op) { -#if 0 - Scene *scene= CTX_data_scene(C); - Object *obedit= CTX_data_edit_object(C); - Mesh *me= obedit->data; - EditMesh *em= BKE_mesh_get_editmesh(me); + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_edit_object(C); + BMEditMesh *em = ((Mesh*)ob->data)->edit_btmesh; + BMOperator bmop; - int selcount = similar_face_select__internal(scene, em, RNA_int_get(op->ptr, "type")); - - if (selcount) { - /* here was an edge-mode only select flush case, has to be generalized */ - EM_selectmode_flush(em); - WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); - BKE_mesh_end_editmesh(me, em); - return OPERATOR_FINISHED; - } - - BKE_mesh_end_editmesh(me, em); -#endif - return OPERATOR_CANCELLED; + /* get the type from RNA */ + int type = RNA_enum_get(op->ptr, "type"); + + float thresh = scene->toolsettings->select_thresh; + + /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */ + EDBM_InitOpf(em, &bmop, op, "similarfaces faces=%hf type=%d thresh=%f", BM_SELECT, type, thresh); + + /* execute the operator */ + BMO_Exec_Op(em->bm, &bmop); + + /* clear the existing selection */ + EDBM_clear_flag_all(em, BM_SELECT); + + /* select the output */ + BMO_HeaderFlag_Buffer(em->bm, &bmop, "facesout", BM_SELECT, BM_ALL); + + /* finish the operator */ + if( !EDBM_FinishOp(em, &bmop, op, 1) ) + return OPERATOR_CANCELLED; + + /* dependencies graph and notification stuff */ + DAG_object_flush_update(scene, ob, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_GEOM_SELECT, ob); + + /* we succeeded */ + return OPERATOR_FINISHED; } /* ***************************************************** */