From 0f4f89f19a664ee300b1da0e71cbf87a3536ebee Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sat, 17 Jan 2009 22:14:08 +0000 Subject: 2.5: uv editor - mouse select, loop select, select linked, unlink selection operators. - added edge selection mode. - fix 2.45 bug with unitialized theme colors, which caused the active face and face centers to be not drawn. --- source/blender/editors/uvedit/uvedit_ops.c | 1999 +++++++++++++++------------- 1 file changed, 1110 insertions(+), 889 deletions(-) (limited to 'source/blender/editors/uvedit/uvedit_ops.c') diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index e097511e466..2e5f64ad6c6 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -28,6 +28,7 @@ */ #include +#include #include #include "MEM_guardedalloc.h" @@ -50,6 +51,7 @@ #include "BKE_image.h" #include "BKE_library.h" #include "BKE_mesh.h" +#include "BKE_report.h" #include "BKE_utildefines.h" #include "IMB_imbuf_types.h" // XXX remove? @@ -64,13 +66,13 @@ #include "WM_api.h" #include "WM_types.h" +#include "UI_view2d.h" + #include "uvedit_intern.h" /* local prototypes */ static void sel_uvco_inside_radius(SpaceImage *sima, Scene *scene, short sel, EditFace *efa, MTFace *tface, int index, float *offset, float *ell, short select_index); void uvedit_selectionCB(SpaceImage *sima, Scene *scene, ARegion *ar, short selecting, Object *obedit, short *mval, float rad); /* used in edit.c */ -void select_edgeloop_tface_uv(bContext *C, Scene *scene, Image *ima, Object *obedit, EditFace *startefa, int starta, int shift, int *flush); -void select_linked_tface_uv(bContext *C, Scene *scene, Image *ima, Object *obedit, int mode); void uvface_setsel__internal(bContext *C, SpaceImage *sima, Scene *scene, Object *obedit, short select); /************************* state testing ************************/ @@ -187,9 +189,9 @@ void ED_uvedit_set_tile(Scene *scene, Object *obedit, Image *ima, int curtile, i // XXX WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); } -/*********************** connection testing *********************/ +/*********************** space conversion *********************/ -static void uvedit_connection_limit(bContext *C, float *limit, float pixellimit) +static void uvedit_pixel_to_float(bContext *C, float *dist, float pixeldist) { ImBuf *ibuf= CTX_data_edit_image_buffer(C); float width, height; @@ -203,8 +205,8 @@ static void uvedit_connection_limit(bContext *C, float *limit, float pixellimit) height= 256.0f; } - limit[0]= pixellimit/width; - limit[1]= pixellimit/height; + dist[0]= pixeldist/width; + dist[1]= pixeldist/height; } /*************** visibility and selection utilities **************/ @@ -249,6 +251,58 @@ void uvedit_face_deselect(Scene *scene, EditFace *efa, MTFace *tf) tf->flag &= ~(TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4); } +int uvedit_edge_selected(Scene *scene, EditFace *efa, MTFace *tf, int i) +{ + int nvert= (efa->v4)? 4: 3; + + if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) { + if(scene->selectmode == SCE_SELECT_FACE) + return (efa->f & SELECT); + else if(scene->selectmode == SCE_SELECT_EDGE) + return (*(&efa->e1 + i))->f & SELECT; + else + return (((efa->v1 + i)->f & SELECT) && ((efa->v1 + (i+1)%nvert)->f & SELECT)); + } + else + return (tf->flag & TF_SEL_MASK(i)) && (tf->flag & TF_SEL_MASK((i+1)%nvert)); +} + +void uvedit_edge_select(Scene *scene, EditFace *efa, MTFace *tf, int i) +{ + int nvert= (efa->v4)? 4: 3; + + if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) { + if(scene->selectmode == SCE_SELECT_FACE) + EM_select_face(efa, 1); + else if(scene->selectmode == SCE_SELECT_EDGE) + EM_select_edge((*(&efa->e1 + i)), 1); + else { + (efa->v1 + i)->f |= SELECT; + (efa->v1 + (i+1)%nvert)->f |= SELECT; + } + } + else + tf->flag |= TF_SEL_MASK(i)|TF_SEL_MASK((i+1)%nvert); +} + +void uvedit_edge_deselect(Scene *scene, EditFace *efa, MTFace *tf, int i) +{ + int nvert= (efa->v4)? 4: 3; + + if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) { + if(scene->selectmode == SCE_SELECT_FACE) + EM_select_face(efa, 0); + else if(scene->selectmode == SCE_SELECT_EDGE) + EM_select_edge((*(&efa->e1 + i)), 0); + else { + (efa->v1 + i)->f &= ~SELECT; + (efa->v1 + (i+1)%nvert)->f &= ~SELECT; + } + } + else + tf->flag &= ~(TF_SEL_MASK(i)|TF_SEL_MASK((i+1)%nvert)); +} + int uvedit_uv_selected(Scene *scene, EditFace *efa, MTFace *tf, int i) { if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) { @@ -445,178 +499,649 @@ void uvedit_constrain_square(Scene *scene, Image *ima, EditMesh *em) } } -/* ******************** mirror operator **************** */ +/************************** find nearest ****************************/ -/* XXX */ -#if 0 - short mode= 0; - mode= pupmenu("Mirror%t|X Axis%x1|Y Axis%x2|"); -#endif +typedef struct NearestHit { + EditFace *efa; + MTFace *tf; -static int mirror_exec(bContext *C, wmOperator *op) + int vert, uv; + int edge, vert2; +} NearestHit; + +static void find_nearest_uv_edge(Scene *scene, Image *ima, EditMesh *em, float co[2], NearestHit *hit) { - float mat[3][3]; - int axis; + MTFace *tf; + EditFace *efa; + EditVert *eve; + float mindist, dist; + int i, nverts; + + mindist= 1e10f; + memset(hit, 0, sizeof(*hit)); + + for(i=0, eve=em->verts.first; eve; eve=eve->next, i++) + eve->tmp.l = i; - Mat3One(mat); - axis= RNA_enum_get(op->ptr, "axis"); + for(efa= em->faces.first; efa; efa= efa->next) { + tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); - if(axis == 'x') { - /* XXX initTransform(TFM_MIRROR, CTX_NO_PET|CTX_AUTOCONFIRM); - BIF_setSingleAxisConstraint(mat[0], " on X axis"); - Transform(); */ - } - else { - /* XXX initTransform(TFM_MIRROR, CTX_NO_PET|CTX_AUTOCONFIRM); - BIF_setSingleAxisConstraint(mat[1], " on Y axis"); - Transform(); */ - } + if(uvedit_face_visible(scene, ima, efa, tf)) { + nverts= efa->v4? 4: 3; - return OPERATOR_FINISHED; + for(i=0; iuv[i], tf->uv[(i+1)%nverts]); + + if(dist < mindist) { + hit->tf= tf; + hit->efa= efa; + hit->edge= i; + mindist= dist; + + hit->vert= (*(&efa->v1 + i))->tmp.l; + hit->vert2= (*(&efa->v1 + ((i+1)%nverts)))->tmp.l; + } + } + } + } } -void UV_OT_mirror(wmOperatorType *ot) +static void find_nearest_uv_face(Scene *scene, Image *ima, EditMesh *em, float co[2], NearestHit *hit) { - static EnumPropertyItem axis_items[] = { - {'x', "MIRROR_X", "Mirror X", "Mirror UVs over X axis."}, - {'y', "MIRROR_Y", "Mirror Y", "Mirror UVs over Y axis."}, - {0, NULL, NULL, NULL}}; + MTFace *tf; + EditFace *efa; + float mindist, dist, cent[2]; + int i, nverts; - /* identifiers */ - ot->name= "Mirror"; - ot->idname= "UV_OT_mirror"; - ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/; + mindist= 1e10f; + memset(hit, 0, sizeof(*hit)); - /* api callbacks */ - ot->exec= mirror_exec; - ot->poll= ED_operator_uvedit; + for(efa= em->faces.first; efa; efa= efa->next) { + tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); - /* properties */ - RNA_def_enum(ot->srna, "axis", axis_items, 'x', "Axis", "Axis to mirror UV locations over."); -} + if(uvedit_face_visible(scene, ima, efa, tf)) { + nverts= efa->v4? 4: 3; + cent[0]= cent[1]= 0.0f; -/* ******************** align operator **************** */ + for(i=0; iuv[i][0]; + cent[1] += tf->uv[i][1]; + } -/* XXX */ -#if 0 -void weld_align_menu_tface_uv(bContext *C) + cent[0] /= nverts; + cent[1] /= nverts; + dist= fabs(co[0]- cent[0]) + fabs(co[1]- cent[1]); + + if(dist < mindist) { + hit->tf= tf; + hit->efa= efa; + mindist= dist; + } + } + } +} + +static int nearest_uv_between(MTFace *tf, int nverts, int id, float co[2], float uv[2]) { - short mode= 0; + float m[3], v1[3], v2[3], c1, c2; + int id1, id2; - mode= pupmenu("Weld/Align%t|Weld%x1|Align Auto%x2|Align X%x3|Align Y%x4"); + id1= (id+nverts-1)%nverts; + id2= (id+nverts+1)%nverts; - if(mode==-1) return; - if(mode==1) weld_align_uv(C, 'w'); - else if(mode==2) weld_align_uv(C, 'a'); - else if(mode==3) weld_align_uv(C, 'x'); - else if(mode==4) weld_align_uv(C, 'y'); + m[0]= co[0]-uv[0]; + m[1]= co[1]-uv[1]; + Vec2Subf(v1, tf->uv[id1], tf->uv[id]); + Vec2Subf(v2, tf->uv[id2], tf->uv[id]); + + /* m and v2 on same side of v-v1? */ + c1= v1[0]*m[1] - v1[1]*m[0]; + c2= v1[0]*v2[1] - v1[1]*v2[0]; + + if(c1*c2 < 0.0f) + return 0; + + /* m and v1 on same side of v-v2? */ + c1= v2[0]*m[1] - v2[1]*m[0]; + c2= v2[0]*v1[1] - v2[1]*v1[0]; + + return (c1*c2 >= 0.0f); } -#endif -static void weld_align_uv(bContext *C, int tool) +static void find_nearest_uv_vert(Scene *scene, Image *ima, EditMesh *em, float co[2], float penalty[2], NearestHit *hit) { - Scene *scene; - Object *obedit; - Image *ima; - EditMesh *em; EditFace *efa; + EditVert *eve; MTFace *tf; - float cent[2], min[2], max[2]; - - scene= CTX_data_scene(C); - obedit= CTX_data_edit_object(C); - em= ((Mesh*)obedit->data)->edit_mesh; - ima= CTX_data_edit_image(C); + float mindist, dist; + int i, nverts; - INIT_MINMAX2(min, max); + mindist= 1e10f; + memset(hit, 0, sizeof(*hit)); + + for(i=0, eve=em->verts.first; eve; eve=eve->next, i++) + eve->tmp.l = i; + + for(efa= em->faces.first; efa; efa= efa->next) { + tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); - if(tool == 'a') { - for(efa= em->faces.first; efa; efa= efa->next) { - tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(uvedit_face_visible(scene, ima, efa, tf)) { + nverts= efa->v4? 4: 3; - if(uvedit_face_visible(scene, ima, efa, tf)) { - if(uvedit_uv_selected(scene, efa, tf, 0)) - DO_MINMAX2(tf->uv[0], min, max) - if(uvedit_uv_selected(scene, efa, tf, 1)) - DO_MINMAX2(tf->uv[1], min, max) - if(uvedit_uv_selected(scene, efa, tf, 2)) - DO_MINMAX2(tf->uv[2], min, max) - if(efa->v4 && uvedit_uv_selected(scene, efa, tf, 3)) - DO_MINMAX2(tf->uv[3], min, max) - } - } + for(i=0; iuv[i][0])+penalty[0] + fabs(co[1]-tf->uv[i][1])+penalty[1]; + else + dist= fabs(co[0]-tf->uv[i][0]) + fabs(co[1]-tf->uv[i][1]); - tool= (max[0]-min[0] >= max[1]-min[1])? 'y': 'x'; - } + if(dist<=mindist) { + if(dist==mindist) + if(!nearest_uv_between(tf, nverts, i, co, tf->uv[i])) + continue; - uvedit_center(scene, ima, obedit, cent, 0); + mindist= dist; - if(tool == 'x' || tool == 'w') { - for(efa= em->faces.first; efa; efa= efa->next) { - tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); - if(uvedit_face_visible(scene, ima, efa, tf)) { - if(uvedit_uv_selected(scene, efa, tf, 0)) - tf->uv[0][0]= cent[0]; - if(uvedit_uv_selected(scene, efa, tf, 1)) - tf->uv[1][0]= cent[0]; - if(uvedit_uv_selected(scene, efa, tf, 2)) - tf->uv[2][0]= cent[0]; - if(efa->v4 && uvedit_uv_selected(scene, efa, tf, 3)) - tf->uv[3][0]= cent[0]; - } - } - } + hit->uv= i; + hit->tf= tf; + hit->efa= efa; - if(tool == 'y' || tool == 'w') { - for(efa= em->faces.first; efa; efa= efa->next) { - tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); - if(uvedit_face_visible(scene, ima, efa, tf)) { - if(uvedit_uv_selected(scene, efa, tf, 0)) - tf->uv[0][1]= cent[1]; - if(uvedit_uv_selected(scene, efa, tf, 1)) - tf->uv[1][1]= cent[1]; - if(uvedit_uv_selected(scene, efa, tf, 2)) - tf->uv[2][1]= cent[1]; - if(efa->v4 && uvedit_uv_selected(scene, efa, tf, 3)) - tf->uv[3][1]= cent[1]; + hit->vert= (*(&efa->v1 + i))->tmp.l; + } } } } - - DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); - WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); // XXX } -static int align_exec(bContext *C, wmOperator *op) +/*********************** loop select ***********************/ + +static void uv_vertex_loop_flag(UvMapVert *first) { - weld_align_uv(C, RNA_enum_get(op->ptr, "axis")); + UvMapVert *iterv; + int count= 0; - return OPERATOR_FINISHED; + for(iterv=first; iterv; iterv=iterv->next) { + if(iterv->separate && iterv!=first) + break; + + count++; + } + + if(count < 5) + first->flag= 1; } -void UV_OT_align(wmOperatorType *ot) +static UvMapVert *uv_vertex_map_get(UvVertMap *vmap, EditFace *efa, int a) { - static EnumPropertyItem axis_items[] = { - {'a', "ALIGN_AUTO", "Align Auto", "Automatically choose the axis on which there is most alignment already."}, - {'x', "ALIGN_X", "Align X", "Align UVs on X axis."}, - {'y', "ALIGN_Y", "Align Y", "Align UVs on Y axis."}, - {0, NULL, NULL, NULL}}; - - /* identifiers */ - ot->name= "Align"; - ot->idname= "UV_OT_align"; - ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/; + UvMapVert *iterv, *first; - /* api callbacks */ - ot->exec= align_exec; - ot->poll= ED_operator_uvedit; + first= EM_get_uv_map_vert(vmap, (*(&efa->v1 + a))->tmp.l); - /* properties */ - RNA_def_enum(ot->srna, "axis", axis_items, 'a', "Axis", "Axis to align UV locations on."); + for(iterv=first; iterv; iterv=iterv->next) { + if(iterv->separate) + first= iterv; + if(iterv->f == efa->tmp.l) + return first; + } + + return NULL; } -/* ******************** weld operator **************** */ - +static int uv_edge_tag_faces(UvMapVert *first1, UvMapVert *first2, int *totface) +{ + UvMapVert *iterv1, *iterv2; + EditFace *efa; + int tot = 0; + + /* count number of faces this edge has */ + for(iterv1=first1; iterv1; iterv1=iterv1->next) { + if(iterv1->separate && iterv1 != first1) + break; + + for(iterv2=first2; iterv2; iterv2=iterv2->next) { + if(iterv2->separate && iterv2 != first2) + break; + + if(iterv1->f == iterv2->f) { + /* if face already tagged, don't do this edge */ + efa= EM_get_face_for_index(iterv1->f); + if(efa->f1) + return 0; + + tot++; + break; + } + } + } + + if(*totface == 0) /* start edge */ + *totface= tot; + else if(tot != *totface) /* check for same number of faces as start edge */ + return 0; + + /* tag the faces */ + for(iterv1=first1; iterv1; iterv1=iterv1->next) { + if(iterv1->separate && iterv1 != first1) + break; + + for(iterv2=first2; iterv2; iterv2=iterv2->next) { + if(iterv2->separate && iterv2 != first2) + break; + + if(iterv1->f == iterv2->f) { + efa= EM_get_face_for_index(iterv1->f); + efa->f1= 1; + break; + } + } + } + + return 1; +} + +static int select_edgeloop(Scene *scene, Image *ima, EditMesh *em, NearestHit *hit, float limit[2], int extend) +{ + EditVert *eve; + EditFace *efa; + MTFace *tf; + UvVertMap *vmap; + UvMapVert *iterv1, *iterv2; + int a, count, looking, nverts, starttotf, select; + + /* setup */ + EM_init_index_arrays(em, 0, 0, 1); + vmap= EM_make_uv_vert_map(em, 0, 0, limit); + + for(count=0, eve=em->verts.first; eve; count++, eve= eve->next) + eve->tmp.l = count; + + for(count=0, efa= em->faces.first; efa; count++, efa= efa->next) { + if(!extend) { + tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + uvedit_face_deselect(scene, efa, tf); + } + + efa->tmp.l= count; + efa->f1= 0; + } + + /* set flags for first face and verts */ + nverts= (hit->efa->v4)? 4: 3; + iterv1= uv_vertex_map_get(vmap, hit->efa, hit->edge); + iterv2= uv_vertex_map_get(vmap, hit->efa, (hit->edge+1)%nverts); + uv_vertex_loop_flag(iterv1); + uv_vertex_loop_flag(iterv2); + + starttotf= 0; + uv_edge_tag_faces(iterv1, iterv2, &starttotf); + + /* sorry, first edge isnt even ok */ + if(iterv1->flag==0 && iterv2->flag==0) looking= 0; + else looking= 1; + + /* iterate */ + while(looking) { + looking= 0; + + /* find correct valence edges which are not tagged yet, but connect to tagged one */ + for(efa= em->faces.first; efa; efa=efa->next) { + tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + if(!efa->f1 && uvedit_face_visible(scene, ima, efa, tf)) { + nverts= (efa->v4)? 4: 3; + for(a=0; a