Index: source/blender/bmesh/bmesh_operators.h =================================================================== --- source/blender/bmesh/bmesh_operators.h (revision 22703) +++ source/blender/bmesh/bmesh_operators.h (working copy) @@ -22,6 +22,7 @@ SUBD_STRAIGHT_CUT, }; +/* similar face selection slot values */ enum { SIMFACE_MATERIAL = 201, SIMFACE_IMAGE, @@ -31,6 +32,24 @@ SIMFACE_COPLANAR, }; +/* similar edge selection slot values */ +enum { + SIMEDGE_LENGTH = 101, + SIMEDGE_DIR, + SIMEDGE_FACE, + SIMEDGE_FACE_ANGLE, + SIMEDGE_CREASE, + SIMEDGE_SEAM, + SIMEDGE_SHARP, +}; + +/* similar vertex selection slot values */ +enum { + SIMVERT_NORMAL = 0, + SIMVERT_FACE, + SIMVERT_VGROUP, +}; + extern BMOpDefine *opdefines[]; extern int bmesh_total_ops; Index: source/blender/bmesh/intern/bmesh_opdefines.c =================================================================== --- source/blender/bmesh/intern/bmesh_opdefines.c (revision 22703) +++ source/blender/bmesh/intern/bmesh_opdefines.c (working copy) @@ -629,6 +629,38 @@ 0 }; +/* + Similar edges select + + Select similar edges (length, direction, edge, seam,....). +*/ +BMOpDefine def_similaredges = { + "similaredges", + {{BMOP_OPSLOT_ELEMENT_BUF, "edges"}, /* input edges */ + {BMOP_OPSLOT_ELEMENT_BUF, "edgeout"}, /* output edges */ + {BMOP_OPSLOT_INT, "type"}, /* type of selection */ + {BMOP_OPSLOT_FLT, "thresh"}, /* threshold of selection */ + {0} /*null-terminating sentinel*/}, + bmesh_similaredges_exec, + 0 +}; + +/* + Similar vertices select + + Select similar vertices (normal, face, vertex group,....). +*/ +BMOpDefine def_similarverts = { + "similarverts", + {{BMOP_OPSLOT_ELEMENT_BUF, "verts"}, /* input vertices */ + {BMOP_OPSLOT_ELEMENT_BUF, "vertout"}, /* output vertices */ + {BMOP_OPSLOT_INT, "type"}, /* type of selection */ + {BMOP_OPSLOT_FLT, "thresh"}, /* threshold of selection */ + {0} /*null-terminating sentinel*/}, + bmesh_similarverts_exec, + 0 +}; + BMOpDefine *opdefines[] = { &def_splitop, &def_dupeop, @@ -670,6 +702,8 @@ &def_pointmerge, &def_collapse, &def_similarfaces, + &def_similaredges, + &def_similarverts, }; 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 22703) +++ source/blender/bmesh/intern/bmesh_operators_private.h (working copy) @@ -47,5 +47,6 @@ void bmesh_pointmerge_exec(BMesh *bm, BMOperator *op); void bmesh_collapse_exec(BMesh *bm, BMOperator *op); void bmesh_similarfaces_exec(BMesh *bm, BMOperator *op); - +void bmesh_similaredges_exec(BMesh *bm, BMOperator *op); +void bmesh_similarverts_exec(BMesh *bm, BMOperator *op); #endif Index: source/blender/bmesh/operators/utils.c =================================================================== --- source/blender/bmesh/operators/utils.c (revision 22703) +++ source/blender/bmesh/operators/utils.c (working copy) @@ -501,7 +501,7 @@ BMIter fm_iter; BMFace *fs, *fm; BMOIter fs_iter; - int num_tex, num_sels = 0, num_total = 0, i = 0, idx = 0; + int num_sels = 0, num_total = 0, i = 0, idx = 0; float angle = 0.0f; tmp_face_ext *f_ext = NULL; int *indices = NULL; @@ -543,7 +543,7 @@ ** 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 ) { + 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: @@ -579,11 +579,9 @@ } /* 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) { + if( !BMO_TestFlag(bm, fm, FACE_MARK) && !BM_TestHFlag(fm, BM_HIDDEN) ) { int cont = 1; for( idx = 0; idx < num_sels && cont == 1; idx++ ) { fs = f_ext[indices[idx]].f; @@ -644,3 +642,313 @@ /* transfer all marked faces to the output slot */ BMO_Flag_To_Slot(bm, op, "faceout", FACE_MARK, BM_FACE); } + +/****************************************************************************** +** Similar Edges +******************************************************************************/ +#define EDGE_MARK 1 + +/* +** compute the angle of an edge (i.e. the angle between two faces) +*/ +static float edge_angle(BMesh *bm, BMEdge *e) +{ + BMIter fiter; + BMFace *f; + int num_faces = 0; + float n1[3], n2[3]; + float angle = 0.0f; + + BM_ITER(f, &fiter, bm, BM_FACES_OF_EDGE, e) { + if( num_faces == 0 ) { + n1[0] = f->no[0]; + n1[1] = f->no[1]; + n1[2] = f->no[2]; + num_faces++; + } else { + n2[0] = f->no[0]; + n2[1] = f->no[1]; + n2[2] = f->no[2]; + num_faces++; + } + } + + angle = VecAngle2(n1, n2) / 180.0; + + return angle; +} +/* +** extra edge information +*/ +typedef struct tmp_edge_ext { + BMEdge *e; + union { + float dir[3]; + float angle; /* angle between the faces*/ + }; + + union { + float length; /* edge length */ + int faces; /* faces count */ + }; +} tmp_edge_ext; + +/* +** select similar edges: the choices are in the enum in source/blender/bmesh/bmesh_operators.h +** choices are length, direction, face, ... +*/ +void bmesh_similaredges_exec(BMesh *bm, BMOperator *op) +{ + BMOIter es_iter; /* selected edges iterator */ + BMIter e_iter; /* mesh edges iterator */ + BMEdge *es; /* selected edge */ + BMEdge *e; /* mesh edge */ + int idx = 0, i = 0, f = 0; + int *indices = NULL; + tmp_edge_ext *e_ext = NULL; + float *angles = NULL; + float angle; + + int num_sels = 0, num_total = 0; + int type = BMO_Get_Int(op, "type"); + float thresh = BMO_Get_Float(op, "thresh"); + + num_total = BM_Count_Element(bm, BM_EDGE); + + /* iterate through all selected edges and mark them */ + BMO_ITER(es, &es_iter, bm, op, "edges", BM_EDGE) { + BMO_SetFlag(bm, es, EDGE_MARK); + num_sels++; + } + + /* allocate memory for the selected edges indices and for all temporary edges */ + indices = (int*)malloc(sizeof(int) * num_sels); + e_ext = (tmp_edge_ext*)malloc(sizeof(tmp_edge_ext) * num_total); + + /* loop through all the edges and fill the edges/indices structure */ + BM_ITER(e, &e_iter, bm, BM_EDGES_OF_MESH, NULL) { + e_ext[i].e = e; + if (BMO_TestFlag(bm, e, EDGE_MARK)) { + indices[idx] = i; + idx++; + } + i++; + } + + /* save us some computation time by doing heavy computation once */ + if( type == SIMEDGE_LENGTH || type == SIMEDGE_FACE || type == SIMEDGE_DIR || + type == SIMEDGE_FACE_ANGLE ) { + for( i = 0; i < num_total; i++ ) { + switch( type ) { + case SIMEDGE_LENGTH: /* compute the length of the edge */ + e_ext[i].length = VecLenf(e_ext[i].e->v1->co, e_ext[i].e->v2->co); + break; + + case SIMEDGE_DIR: /* compute the direction */ + VecSubf(e_ext[i].dir, e_ext[i].e->v1->co, e_ext[i].e->v2->co); + break; + + case SIMEDGE_FACE: /* count the faces around the edge */ + e_ext[i].faces = BM_Edge_FaceCount(e_ext[i].e); + break; + + case SIMEDGE_FACE_ANGLE: + e_ext[i].faces = BM_Edge_FaceCount(e_ext[i].e); + if( e_ext[i].faces == 2 ) + e_ext[i].angle = edge_angle(bm, e_ext[i].e); + break; + } + } + } + + /* select the edges if any */ + for( i = 0; i < num_total; i++ ) { + e = e_ext[i].e; + if( !BMO_TestFlag(bm, e, EDGE_MARK) && !BM_TestHFlag(e, BM_HIDDEN) ) { + int cont = 1; + for( idx = 0; idx < num_sels && cont == 1; idx++ ) { + es = e_ext[indices[idx]].e; + switch( type ) { + case SIMEDGE_LENGTH: + if( fabs(e_ext[i].length - e_ext[indices[idx]].length) <= thresh ) { + BMO_SetFlag(bm, e, EDGE_MARK); + cont = 0; + } + break; + + case SIMEDGE_DIR: + /* compute the angle between the two edges */ + angle = VecAngle2(e_ext[i].dir, e_ext[indices[idx]].dir); + + if( angle > 90.0 ) /* use the smallest angle between the edges */ + angle = fabs(angle - 180.0f); + + if( angle / 90.0 <= thresh ) { + BMO_SetFlag(bm, e, EDGE_MARK); + cont = 0; + } + break; + + case SIMEDGE_FACE: + if( e_ext[i].faces == e_ext[indices[idx]].faces ) { + BMO_SetFlag(bm, e, EDGE_MARK); + cont = 0; + } + break; + + case SIMEDGE_FACE_ANGLE: + if( e_ext[i].faces == 2 ) { + if( e_ext[indices[idx]].faces == 2 ) { + if( fabs(e_ext[i].angle - e_ext[indices[idx]].angle) <= thresh ) { + BMO_SetFlag(bm, e, EDGE_MARK); + cont = 0; + } + } + } else cont = 0; + break; + + case SIMEDGE_CREASE: + if( fabs(e->crease - es->crease) <= thresh ) { + BMO_SetFlag(bm, e, EDGE_MARK); + cont = 0; + } + break; + + case SIMEDGE_SEAM: + if( BM_TestHFlag(e, BM_SEAM) == BM_TestHFlag(es, BM_SEAM) ) { + BMO_SetFlag(bm, e, EDGE_MARK); + cont = 0; + } + break; + + case SIMEDGE_SHARP: + if( BM_TestHFlag(e, BM_SHARP) == BM_TestHFlag(es, BM_SHARP) ) { + BMO_SetFlag(bm, e, EDGE_MARK); + cont = 0; + } + break; + } + } + } + } + + free(e_ext); + free(indices); + + /* transfer all marked edges to the output slot */ + BMO_Flag_To_Slot(bm, op, "edgeout", EDGE_MARK, BM_EDGE); +} + +/****************************************************************************** +** Similar Vertices +******************************************************************************/ +#define VERT_MARK 1 + +typedef struct tmp_vert_ext { + BMVert *v; + union { + int num_faces; /* adjacent faces */ + MDeformVert *dvert; /* deform vertex */ + }; +} tmp_vert_ext; + +/* +** select similar vertices: the choices are in the enum in source/blender/bmesh/bmesh_operators.h +** choices are normal, face, vertex group... +*/ +void bmesh_similarverts_exec(BMesh *bm, BMOperator *op) +{ + BMOIter vs_iter; /* selected verts iterator */ + BMIter v_iter; /* mesh verts iterator */ + BMVert *vs; /* selected vertex */ + BMVert *v; /* mesh vertex */ + tmp_vert_ext *v_ext = NULL; + int *indices = NULL; + int num_total = 0, num_sels = 0, i = 0, idx = 0; + int type = BMO_Get_Int(op, "type"); + float thresh = BMO_Get_Float(op, "thresh"); + + num_total = BM_Count_Element(bm, BM_VERT); + + /* iterate through all selected edges and mark them */ + BMO_ITER(vs, &vs_iter, bm, op, "verts", BM_VERT) { + BMO_SetFlag(bm, vs, VERT_MARK); + num_sels++; + } + + /* allocate memory for the selected vertices indices and for all temporary vertices */ + indices = (int*)MEM_mallocN(sizeof(int) * num_sels, "vertex indices"); + v_ext = (tmp_vert_ext*)MEM_mallocN(sizeof(tmp_vert_ext) * num_total, "vertex extra"); + + /* loop through all the vertices and fill the vertices/indices structure */ + BM_ITER(v, &v_iter, bm, BM_VERTS_OF_MESH, NULL) { + v_ext[i].v = v; + if (BMO_TestFlag(bm, v, VERT_MARK)) { + indices[idx] = i; + idx++; + } + + switch( type ) { + case SIMVERT_FACE: + /* calling BM_Vert_FaceCount every time is time consumming, so call it only once per vertex */ + v_ext[i].num_faces = BM_Vert_FaceCount(v); + break; + + case SIMVERT_VGROUP: + if( CustomData_has_layer(&(bm->vdata),CD_MDEFORMVERT) ) { + v_ext[i].dvert = CustomData_bmesh_get(&bm->vdata, v_ext[i].v->head.data, CD_MDEFORMVERT); + } else v_ext[i].dvert = NULL; + break; + } + + i++; + } + + /* select the vertices if any */ + for( i = 0; i < num_total; i++ ) { + v = v_ext[i].v; + if( !BMO_TestFlag(bm, v, VERT_MARK) && !BM_TestHFlag(v, BM_HIDDEN) ) { + int cont = 1; + for( idx = 0; idx < num_sels && cont == 1; idx++ ) { + vs = v_ext[indices[idx]].v; + switch( type ) { + case SIMVERT_NORMAL: + /* compare the angle between the normals */ + if( VecAngle2(v->no, vs->no) / 180.0 <= thresh ) { + BMO_SetFlag(bm, v, VERT_MARK); + cont = 0; + + } + break; + case SIMVERT_FACE: + /* number of adjacent faces */ + if( v_ext[i].num_faces == v_ext[indices[idx]].num_faces ) { + BMO_SetFlag(bm, v, VERT_MARK); + cont = 0; + } + break; + + case SIMVERT_VGROUP: + if( v_ext[i].dvert != NULL && v_ext[indices[idx]].dvert != NULL ) { + int v1, v2; + for( v1 = 0; v1 < v_ext[i].dvert->totweight && cont == 1; v1++ ) { + for( v2 = 0; v2 < v_ext[indices[idx]].dvert->totweight; v2++ ) { + if( v_ext[i].dvert->dw[v1].def_nr == v_ext[indices[idx]].dvert->dw[v2].def_nr ) { + BMO_SetFlag(bm, v, VERT_MARK); + cont = 0; + break; + } + } + } + } + break; + } + } + } + } + + MEM_freeN(indices); + MEM_freeN(v_ext); + + BMO_Flag_To_Slot(bm, op, "vertout", VERT_MARK, BM_VERT); +} Index: source/blender/editors/mesh/bmesh_select.c =================================================================== --- source/blender/editors/mesh/bmesh_select.c (revision 22703) +++ source/blender/editors/mesh/bmesh_select.c (working copy) @@ -699,18 +699,10 @@ /* EDGE GROUP */ -#define SIMEDGE_LENGTH 101 -#define SIMEDGE_DIR 102 -#define SIMEDGE_FACE 103 -#define SIMEDGE_FACE_ANGLE 104 -#define SIMEDGE_CREASE 105 -#define SIMEDGE_SEAM 106 -#define SIMEDGE_SHARP 107 - static EnumPropertyItem prop_simedge_types[] = { {SIMEDGE_LENGTH, "LENGTH", 0, "Length", ""}, {SIMEDGE_DIR, "DIR", 0, "Direction", ""}, - {SIMEDGE_FACE, "FACE", 0, "Amount of Vertices in Face", ""}, + {SIMEDGE_FACE, "FACE", 0, "Amount of Faces Around an Edge", ""}, {SIMEDGE_FACE_ANGLE, "FACE_ANGLE", 0, "Face Angles", ""}, {SIMEDGE_CREASE, "CREASE", 0, "Crease", ""}, {SIMEDGE_SEAM, "SEAM", 0, "Seam", ""}, @@ -920,6 +912,44 @@ /* wrap the above function but do selection flushing edge to face */ static int similar_edge_select_exec(bContext *C, wmOperator *op) { + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_edit_object(C); + BMEditMesh *em = ((Mesh*)ob->data)->edit_btmesh; + BMOperator bmop; + + /* 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, "similaredges edges=%he 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, "edgeout", BM_SELECT, BM_ALL); + + + /* finish the operator */ + if( !EDBM_FinishOp(em, &bmop, op, 1) ) + return OPERATOR_CANCELLED; + + /* now select faces if edges form closed loops */ + EDBM_selectmode_flush(em); + + /* 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; #if 0 Scene *scene= CTX_data_scene(C); Object *obedit= CTX_data_edit_object(C); @@ -950,13 +980,9 @@ mode 3: same vertex groups */ -#define SIMVERT_NORMAL 0 -#define SIMVERT_FACE 1 -#define SIMVERT_VGROUP 2 - static EnumPropertyItem prop_simvertex_types[] = { {SIMVERT_NORMAL, "NORMAL", 0, "Normal", ""}, - {SIMVERT_FACE, "FACE", 0, "Amount of Vertices in Face", ""}, + {SIMVERT_FACE, "FACE", 0, "Amount of Adjacent Faces", ""}, {SIMVERT_VGROUP, "VGROUP", 0, "Vertex Groups", ""}, {0, NULL, 0, NULL, NULL} }; @@ -964,6 +990,44 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op) { + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_edit_object(C); + BMEditMesh *em = ((Mesh*)ob->data)->edit_btmesh; + BMOperator bmop; + + /* 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, "similarverts verts=%hv 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, "vertout", BM_SELECT, BM_ALL); + + + /* finish the operator */ + if( !EDBM_FinishOp(em, &bmop, op, 1) ) + return OPERATOR_CANCELLED; + + /* now select faces if vertices form closed loops */ + EDBM_selectmode_flush(em); + + /* 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; #if 0 Scene *scene= CTX_data_scene(C); Object *obedit= CTX_data_edit_object(C);