4 * ***** BEGIN GPL LICENSE BLOCK *****
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 * The Original Code is Copyright (C) 2004 Blender Foundation.
21 * All rights reserved.
23 * The Original Code is: all of this file.
25 * Contributor(s): none yet.
27 * ***** END GPL LICENSE BLOCK *****
32 editmesh_mods.c, UI level access, no geometry changes
40 #include "MEM_guardedalloc.h"
42 #include "MTC_matrixops.h"
44 #include "DNA_mesh_types.h"
45 #include "DNA_material_types.h"
46 #include "DNA_meshdata_types.h"
47 #include "DNA_modifier_types.h"
48 #include "DNA_object_types.h"
49 #include "DNA_texture_types.h"
50 #include "DNA_scene_types.h"
51 #include "DNA_screen_types.h"
52 #include "DNA_space_types.h"
53 #include "DNA_view3d_types.h"
55 #include "BLI_blenlib.h"
56 #include "BLI_arithb.h"
57 #include "BLI_editVert.h"
60 #include "BKE_context.h"
61 #include "BKE_displist.h"
62 #include "BKE_depsgraph.h"
63 #include "BKE_DerivedMesh.h"
64 #include "BKE_customdata.h"
65 #include "BKE_global.h"
67 #include "BKE_material.h"
68 #include "BKE_texture.h"
69 #include "BKE_utildefines.h"
71 #include "IMB_imbuf_types.h"
72 #include "IMB_imbuf.h"
74 #include "RE_render_ext.h" /* externtex */
79 #include "RNA_access.h"
80 #include "RNA_define.h"
83 #include "ED_screen.h"
84 #include "ED_view3d.h"
87 #include "BIF_glutil.h"
89 #include "mesh_intern.h"
91 #include "BLO_sys_types.h" // for intptr_t support
94 static void waitcursor() {}
95 static void error() {}
96 static int pupmenu() {return 0;}
98 /* ****************************** MIRROR **************** */
100 void EM_select_mirrored(Object *obedit, EditMesh *em)
102 if(em->selectmode & SCE_SELECT_VERTEX) {
105 for(eve= em->verts.first; eve; eve= eve->next) {
106 if(eve->f & SELECT) {
107 v1= editmesh_get_x_mirror_vert(obedit, em, eve->co);
117 void EM_automerge(int update)
121 // if ((scene->automerge) &&
122 // (obedit && obedit->type==OB_MESH) &&
123 // (((Mesh*)obedit->data)->mr==NULL)
125 // len = removedoublesflag(1, 1, scene->toolsettings->doublimit);
127 // em->totvert -= len; /* saves doing a countall */
129 // DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
135 /* ****************************** SELECTION ROUTINES **************** */
137 unsigned int em_solidoffs=0, em_wireoffs=0, em_vertoffs=0; /* set in drawobject.c ... for colorindices */
139 /* facilities for border select and circle select */
140 static char *selbuf= NULL;
142 /* opengl doesn't support concave... */
143 static void draw_triangulated(short mcords[][2], short tot)
145 ListBase lb={NULL, NULL};
151 dl= MEM_callocN(sizeof(DispList), "poly disp");
155 dl->verts= fp= MEM_callocN(tot*3*sizeof(float), "poly verts");
156 BLI_addtail(&lb, dl);
158 for(a=0; a<tot; a++, fp+=3) {
159 fp[0]= (float)mcords[a][0];
160 fp[1]= (float)mcords[a][1];
164 filldisplist(&lb, &lb);
167 dl= lb.first; /* filldisplist adds in head of list */
168 if(dl->type==DL_INDEX3) {
174 glBegin(GL_TRIANGLES);
176 glVertex3fv(fp+3*index[0]);
177 glVertex3fv(fp+3*index[1]);
178 glVertex3fv(fp+3*index[2]);
188 /* reads rect, and builds selection array for quick lookup */
189 /* returns if all is OK */
190 int EM_init_backbuf_border(ViewContext *vc, short xmin, short ymin, short xmax, short ymax)
196 if(vc->obedit==NULL || vc->v3d->drawtype<OB_SOLID || (vc->v3d->flag & V3D_ZBUF_SELECT)==0) return 0;
198 buf= view3d_read_backbuf(vc, xmin, ymin, xmax, ymax);
199 if(buf==NULL) return 0;
200 if(em_vertoffs==0) return 0;
204 /* build selection lookup */
205 selbuf= MEM_callocN(em_vertoffs+1, "selbuf");
207 a= (xmax-xmin+1)*(ymax-ymin+1);
209 if(*dr>0 && *dr<=em_vertoffs)
217 int EM_check_backbuf(unsigned int index)
219 if(selbuf==NULL) return 1;
220 if(index>0 && index<=em_vertoffs)
221 return selbuf[index];
225 void EM_free_backbuf(void)
227 if(selbuf) MEM_freeN(selbuf);
231 /* mcords is a polygon mask
233 - draw with black in backbuffer,
234 - grab again and compare
237 int EM_mask_init_backbuf_border(ViewContext *vc, short mcords[][2], short tot, short xmin, short ymin, short xmax, short ymax)
239 unsigned int *dr, *drm;
240 struct ImBuf *buf, *bufmask;
243 /* method in use for face selecting too */
244 if(vc->obedit==NULL) {
245 if(FACESEL_PAINT_TEST);
248 else if(vc->v3d->drawtype<OB_SOLID || (vc->v3d->flag & V3D_ZBUF_SELECT)==0) return 0;
250 buf= view3d_read_backbuf(vc, xmin, ymin, xmax, ymax);
251 if(buf==NULL) return 0;
252 if(em_vertoffs==0) return 0;
257 glDisable(GL_DEPTH_TEST);
261 /* yah, opengl doesn't do concave... tsk! */
262 ED_region_pixelspace(vc->ar);
263 draw_triangulated(mcords, tot);
265 glBegin(GL_LINE_LOOP); /* for zero sized masks, lines */
266 for(a=0; a<tot; a++) glVertex2s(mcords[a][0], mcords[a][1]);
269 glFinish(); /* to be sure readpixels sees mask */
272 bufmask= view3d_read_backbuf(vc, xmin, ymin, xmax, ymax);
274 if(bufmask==NULL) return 0; /* only when mem alloc fails, go crash somewhere else! */
276 /* build selection lookup */
277 selbuf= MEM_callocN(em_vertoffs+1, "selbuf");
279 a= (xmax-xmin+1)*(ymax-ymin+1);
281 if(*dr>0 && *dr<=em_vertoffs && *drm==0) selbuf[*dr]= 1;
285 IMB_freeImBuf(bufmask);
290 /* circle shaped sample area */
291 int EM_init_backbuf_circle(ViewContext *vc, short xs, short ys, short rads)
295 short xmin, ymin, xmax, ymax, xc, yc;
298 /* method in use for face selecting too */
299 if(vc->obedit==NULL) {
300 if(FACESEL_PAINT_TEST);
303 else if(vc->v3d->drawtype<OB_SOLID || (vc->v3d->flag & V3D_ZBUF_SELECT)==0) return 0;
305 xmin= xs-rads; xmax= xs+rads;
306 ymin= ys-rads; ymax= ys+rads;
307 buf= view3d_read_backbuf(vc, xmin, ymin, xmax, ymax);
308 if(em_vertoffs==0) return 0;
309 if(buf==NULL) return 0;
313 /* build selection lookup */
314 selbuf= MEM_callocN(em_vertoffs+1, "selbuf");
316 for(yc= -rads; yc<=rads; yc++) {
317 for(xc= -rads; xc<=rads; xc++, dr++) {
318 if(xc*xc + yc*yc < radsq) {
319 if(*dr>0 && *dr<=em_vertoffs) selbuf[*dr]= 1;
329 static void findnearestvert__doClosest(void *userData, EditVert *eve, int x, int y, int index)
331 struct { short mval[2], pass, select, strict; int dist, lastIndex, closestIndex; EditVert *closest; } *data = userData;
334 if (index<=data->lastIndex)
337 if (index>data->lastIndex)
342 int temp = abs(data->mval[0] - x) + abs(data->mval[1]- y);
343 if ((eve->f&1) == data->select) {
344 if (data->strict == 1)
350 if (temp<data->dist) {
353 data->closestIndex = index;
361 static unsigned int findnearestvert__backbufIndextest(void *handle, unsigned int index)
363 EditMesh *em= (EditMesh *)handle;
364 EditVert *eve = BLI_findlink(&em->verts, index-1);
366 if(eve && (eve->f & SELECT)) return 0;
372 * dist (in/out): minimal distance to the nearest and at the end, actual distance
373 * sel: selection bias
374 * if SELECT, selected vertice are given a 5 pixel bias to make them farter than unselect verts
375 * if 0, unselected vertice are given the bias
376 * strict: if 1, the vertice corresponding to the sel parameter are ignored and not just biased
378 EditVert *findnearestvert(ViewContext *vc, int *dist, short sel, short strict)
380 if(vc->v3d->drawtype>OB_WIRE && (vc->v3d->flag & V3D_ZBUF_SELECT)){
385 if(strict) index = view3d_sample_backbuf_rect(vc, vc->mval, 50, em_wireoffs, 0xFFFFFF, &distance, strict, vc->em, findnearestvert__backbufIndextest);
386 else index = view3d_sample_backbuf_rect(vc, vc->mval, 50, em_wireoffs, 0xFFFFFF, &distance, 0, NULL, NULL);
388 eve = BLI_findlink(&vc->em->verts, index-1);
390 if(eve && distance < *dist) {
399 struct { short mval[2], pass, select, strict; int dist, lastIndex, closestIndex; EditVert *closest; } data;
400 static int lastSelectedIndex=0;
401 static EditVert *lastSelected=NULL;
403 if (lastSelected && BLI_findlink(&vc->em->verts, lastSelectedIndex)!=lastSelected) {
404 lastSelectedIndex = 0;
408 data.lastIndex = lastSelectedIndex;
409 data.mval[0] = vc->mval[0];
410 data.mval[1] = vc->mval[1];
413 data.strict = strict;
415 data.closestIndex = 0;
418 mesh_foreachScreenVert(vc, findnearestvert__doClosest, &data, 1);
422 mesh_foreachScreenVert(vc, findnearestvert__doClosest, &data, 1);
426 lastSelected = data.closest;
427 lastSelectedIndex = data.closestIndex;
433 /* returns labda for closest distance v1 to line-piece v2-v3 */
434 static float labda_PdistVL2Dfl( float *v1, float *v2, float *v3)
440 len= rc[0]*rc[0]+ rc[1]*rc[1];
444 return ( rc[0]*(v1[0]-v2[0]) + rc[1]*(v1[1]-v2[1]) )/len;
447 /* note; uses v3d, so needs active 3d window */
448 static void findnearestedge__doClosest(void *userData, EditEdge *eed, int x0, int y0, int x1, int y1, int index)
450 struct { ViewContext vc; float mval[2]; int dist; EditEdge *closest; } *data = userData;
459 distance= PdistVL2Dfl(data->mval, v1, v2);
461 if(eed->f & SELECT) distance+=5;
462 if(distance < data->dist) {
463 if(data->vc.rv3d->rflag & RV3D_CLIPPING) {
464 float labda= labda_PdistVL2Dfl(data->mval, v1, v2);
467 vec[0]= eed->v1->co[0] + labda*(eed->v2->co[0] - eed->v1->co[0]);
468 vec[1]= eed->v1->co[1] + labda*(eed->v2->co[1] - eed->v1->co[1]);
469 vec[2]= eed->v1->co[2] + labda*(eed->v2->co[2] - eed->v1->co[2]);
470 Mat4MulVecfl(data->vc.obedit->obmat, vec);
472 if(view3d_test_clipping(data->vc.rv3d, vec)==0) {
473 data->dist = distance;
478 data->dist = distance;
483 EditEdge *findnearestedge(ViewContext *vc, int *dist)
486 if(vc->v3d->drawtype>OB_WIRE && (vc->v3d->flag & V3D_ZBUF_SELECT)) {
488 unsigned int index = view3d_sample_backbuf_rect(vc, vc->mval, 50, em_solidoffs, em_wireoffs, &distance,0, NULL, NULL);
489 EditEdge *eed = BLI_findlink(&vc->em->edges, index-1);
491 if (eed && distance<*dist) {
499 struct { ViewContext vc; float mval[2]; int dist; EditEdge *closest; } data;
502 data.mval[0] = vc->mval[0];
503 data.mval[1] = vc->mval[1];
507 mesh_foreachScreenEdge(vc, findnearestedge__doClosest, &data, 2);
514 static void findnearestface__getDistance(void *userData, EditFace *efa, int x, int y, int index)
516 struct { short mval[2]; int dist; EditFace *toFace; } *data = userData;
518 if (efa==data->toFace) {
519 int temp = abs(data->mval[0]-x) + abs(data->mval[1]-y);
525 static void findnearestface__doClosest(void *userData, EditFace *efa, int x, int y, int index)
527 struct { short mval[2], pass; int dist, lastIndex, closestIndex; EditFace *closest; } *data = userData;
530 if (index<=data->lastIndex)
533 if (index>data->lastIndex)
538 int temp = abs(data->mval[0]-x) + abs(data->mval[1]-y);
540 if (temp<data->dist) {
543 data->closestIndex = index;
547 static EditFace *findnearestface(ViewContext *vc, int *dist)
550 if(vc->v3d->drawtype>OB_WIRE && (vc->v3d->flag & V3D_ZBUF_SELECT)) {
551 unsigned int index = view3d_sample_backbuf(vc, vc->mval[0], vc->mval[1]);
552 EditFace *efa = BLI_findlink(&vc->em->faces, index-1);
555 struct { short mval[2]; int dist; EditFace *toFace; } data;
557 data.mval[0] = vc->mval[0];
558 data.mval[1] = vc->mval[1];
559 data.dist = 0x7FFF; /* largest short */
562 mesh_foreachScreenFace(vc, findnearestface__getDistance, &data);
564 if(vc->em->selectmode == SCE_SELECT_FACE || data.dist<*dist) { /* only faces, no dist check */
573 struct { short mval[2], pass; int dist, lastIndex, closestIndex; EditFace *closest; } data;
574 static int lastSelectedIndex=0;
575 static EditFace *lastSelected=NULL;
577 if (lastSelected && BLI_findlink(&vc->em->faces, lastSelectedIndex)!=lastSelected) {
578 lastSelectedIndex = 0;
582 data.lastIndex = lastSelectedIndex;
583 data.mval[0] = vc->mval[0];
584 data.mval[1] = vc->mval[1];
587 data.closestIndex = 0;
590 mesh_foreachScreenFace(vc, findnearestface__doClosest, &data);
594 mesh_foreachScreenFace(vc, findnearestface__doClosest, &data);
598 lastSelected = data.closest;
599 lastSelectedIndex = data.closestIndex;
605 /* best distance based on screen coords.
606 use em->selectmode to define how to use
607 selected vertices and edges get disadvantage
608 return 1 if found one
610 static int unified_findnearest(ViewContext *vc, EditVert **eve, EditEdge **eed, EditFace **efa)
612 EditMesh *em= vc->em;
619 /* no afterqueue (yet), so we check it now, otherwise the em_xxxofs indices are bad */
620 view3d_validate_backbuf(vc);
622 if(em->selectmode & SCE_SELECT_VERTEX)
623 *eve= findnearestvert(vc, &dist, SELECT, 0);
624 if(em->selectmode & SCE_SELECT_FACE)
625 *efa= findnearestface(vc, &dist);
627 dist-= 20; /* since edges select lines, we give dots advantage of 20 pix */
628 if(em->selectmode & SCE_SELECT_EDGE)
629 *eed= findnearestedge(vc, &dist);
631 /* return only one of 3 pointers, for frontbuffer redraws */
633 *efa= NULL; *eve= NULL;
639 return (*eve || *eed || *efa);
643 /* **************** SIMILAR "group" SELECTS. FACE, EDGE AND VERTEX ************** */
645 /* selects new faces/edges/verts based on the
649 mode 1: same material
652 mode 4: same perimeter
654 mode 6: same co-planer
657 static EnumPropertyItem prop_simface_types[] = {
658 {1, "MATERIAL", "Material", ""},
659 {2, "IMAGE", "Image", ""},
660 {3, "AREA", "Area", ""},
661 {4, "PERIMETER", "Perimeter", ""},
662 {5, "NORMAL", "Normal", ""},
663 {6, "COPLANAR", "Co-planar", ""},
664 {0, NULL, NULL, NULL}
668 /* this as a way to compare the ares, perim of 2 faces thay will scale to different sizes
669 *0.5 so smaller faces arnt ALWAYS selected with a thresh of 1.0 */
670 #define SCALE_CMP(a,b) ((a+a*thresh >= b) && (a-(a*thresh*0.5) <= b))
672 static int similar_face_select__internal(Scene *scene, EditMesh *em, int mode)
674 EditFace *efa, *base_efa=NULL;
675 unsigned int selcount=0; /*count how many new faces we select*/
677 /*deselcount, count how many deselected faces are left, so we can bail out early
678 also means that if there are no deselected faces, we can avoid a lot of looping */
679 unsigned int deselcount=0;
680 float thresh= scene->toolsettings->select_thresh;
683 for(efa= em->faces.first; efa; efa= efa->next) {
685 if (efa->f & SELECT) {
690 deselcount++; /* a deselected face we may select later */
695 if (!ok || !deselcount) /* no data selected OR no more data to select */
698 /*if mode is 3 then record face areas, 4 record perimeter */
700 for(efa= em->faces.first; efa; efa= efa->next) {
701 efa->tmp.fp= EM_face_area(efa);
703 } else if (mode==4) {
704 for(efa= em->faces.first; efa; efa= efa->next) {
705 efa->tmp.fp= EM_face_perimeter(efa);
709 for(base_efa= em->faces.first; base_efa; base_efa= base_efa->next) {
710 if (base_efa->f1) { /* This was one of the faces originaly selected */
711 if (mode==1) { /* same material */
712 for(efa= em->faces.first; efa; efa= efa->next) {
714 !(efa->f & SELECT) &&
716 base_efa->mat_nr == efa->mat_nr
718 EM_select_face(efa, 1);
721 if (!deselcount) /*have we selected all posible faces?, if so return*/
725 } else if (mode==2) { /* same image */
726 MTFace *tf, *base_tf;
728 base_tf = (MTFace*)CustomData_em_get(&em->fdata, base_efa->data,
734 for(efa= em->faces.first; efa; efa= efa->next) {
735 if (!(efa->f & SELECT) && !efa->h) {
736 tf = (MTFace*)CustomData_em_get(&em->fdata, efa->data,
739 if(base_tf->tpage == tf->tpage) {
740 EM_select_face(efa, 1);
743 if (!deselcount) /*have we selected all posible faces?, if so return*/
748 } else if (mode==3 || mode==4) { /* same area OR same perimeter, both use the same temp var */
749 for(efa= em->faces.first; efa; efa= efa->next) {
751 (!(efa->f & SELECT) && !efa->h) &&
752 SCALE_CMP(base_efa->tmp.fp, efa->tmp.fp)
754 EM_select_face(efa, 1);
757 if (!deselcount) /*have we selected all posible faces?, if so return*/
761 } else if (mode==5) { /* same normal */
763 for(efa= em->faces.first; efa; efa= efa->next) {
764 if (!(efa->f & SELECT) && !efa->h) {
765 angle= VecAngle2(base_efa->n, efa->n);
766 if (angle/180.0<=thresh) {
767 EM_select_face(efa, 1);
770 if (!deselcount) /*have we selected all posible faces?, if so return*/
775 } else if (mode==6) { /* same planer */
776 float angle, base_dot, dot;
777 base_dot= Inpf(base_efa->cent, base_efa->n);
778 for(efa= em->faces.first; efa; efa= efa->next) {
779 if (!(efa->f & SELECT) && !efa->h) {
780 angle= VecAngle2(base_efa->n, efa->n);
781 if (angle/180.0<=thresh) {
782 dot=Inpf(efa->cent, base_efa->n);
783 if (fabs(base_dot-dot) <= thresh) {
784 EM_select_face(efa, 1);
787 if (!deselcount) /*have we selected all posible faces?, if so return*/
795 } /* end base_efa loop */
799 static int similar_face_select_exec(bContext *C, wmOperator *op)
801 Scene *scene= CTX_data_scene(C);
802 Object *obedit= CTX_data_edit_object(C);
803 Mesh *me= obedit->data;
804 EditMesh *em= me->edit_mesh;
806 int selcount = similar_face_select__internal(scene, em, RNA_int_get(op->ptr, "type"));
809 /* here was an edge-mode only select flush case, has to be generalized */
810 EM_selectmode_flush(em);
811 WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
812 return OPERATOR_FINISHED;
815 return OPERATOR_CANCELLED;
818 void MESH_OT_similar_face_select(wmOperatorType *ot)
821 ot->name= "Similar Face Select";
822 ot->idname= "MESH_OT_similar_face_select";
825 ot->invoke= WM_menu_invoke;
826 ot->exec= similar_face_select_exec;
827 ot->poll= ED_operator_editmesh;
830 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
833 RNA_def_enum(ot->srna, "type", prop_simface_types, 0, "Type", "");
836 /* ***************************************************** */
841 mode 2: same direction
842 mode 3: same number of face users
843 mode 4: similar face angles.
844 mode 5: similar crease
846 mode 7: similar sharp
849 static EnumPropertyItem prop_simedge_types[] = {
850 {1, "LENGTH", "Length", ""},
851 {2, "DIR", "Direction", ""},
852 {3, "FACE", "Amount of Vertices in Face", ""},
853 {4, "FACE_ANGLE", "Face Angles", ""},
854 {5, "CREASE", "Crease", ""},
855 {6, "SEAM", "Seam", ""},
856 {7, "SHARP", "Sharpness", ""},
857 {0, NULL, NULL, NULL}
860 static int similar_edge_select__internal(Scene *scene, EditMesh *em, int mode)
862 EditEdge *eed, *base_eed=NULL;
863 unsigned int selcount=0; /* count how many new edges we select*/
865 /*count how many visible selected edges there are,
866 so we can return when there are none left */
867 unsigned int deselcount=0;
870 float thresh= scene->toolsettings->select_thresh;
872 for(eed= em->edges.first; eed; eed= eed->next) {
874 if (eed->f & SELECT) {
881 /* set all eed->tmp.l to 0 we use it later.
882 for counting face users*/
884 eed->f2=0; /* only for mode 4, edge animations */
888 if (!ok || !deselcount) /* no data selected OR no more data to select*/
891 if (mode==1) { /*store length*/
892 for(eed= em->edges.first; eed; eed= eed->next) {
893 if (!eed->h) /* dont calc data for hidden edges*/
894 eed->tmp.fp= VecLenf(eed->v1->co, eed->v2->co);
896 } else if (mode==3) { /*store face users*/
898 /* cound how many faces each edge uses use tmp->l */
899 for(efa= em->faces.first; efa; efa= efa->next) {
903 if (efa->e4) efa->e4->tmp.l++;
905 } else if (mode==4) { /*store edge angles */
908 /* cound how many faces each edge uses use tmp.l */
909 for(efa= em->faces.first; efa; efa= efa->next) {
910 /* here we use the edges temp data to assign a face
911 if a face has alredy been assigned (eed->f2==1)
912 we calculate the angle between the current face and
913 the edges previously found face.
914 store the angle in eed->tmp.fp (loosing the face eed->tmp.f)
915 but tagging eed->f2==2, so we know not to look at it again.
916 This only works for edges that connect to 2 faces. but its good enough
919 /* se we can loop through face edges*/
923 if (j==1) eed= efa->e2;
924 else if (j==2) eed= efa->e3;
931 if (!eed->h) { /* dont calc data for hidden edges*/
934 else if (eed->f2==0) /* first access, assign the face */
936 else if (eed->f2==1) /* second, we assign the angle*/
937 eed->tmp.fp= VecAngle2(eed->tmp.f->n, efa->n)/180;
938 eed->f2++; /* f2==0 no face assigned. f2==1 one face found. f2==2 angle calculated.*/
945 for(base_eed= em->edges.first; base_eed; base_eed= base_eed->next) {
947 if (mode==1) { /* same length */
948 for(eed= em->edges.first; eed; eed= eed->next) {
950 !(eed->f & SELECT) &&
952 SCALE_CMP(base_eed->tmp.fp, eed->tmp.fp)
954 EM_select_edge(eed, 1);
957 if (!deselcount) /*have we selected all posible faces?, if so return*/
961 } else if (mode==2) { /* same direction */
962 float base_dir[3], dir[3], angle;
963 VecSubf(base_dir, base_eed->v1->co, base_eed->v2->co);
964 for(eed= em->edges.first; eed; eed= eed->next) {
965 if (!(eed->f & SELECT) && !eed->h) {
966 VecSubf(dir, eed->v1->co, eed->v2->co);
967 angle= VecAngle2(base_dir, dir);
969 if (angle>90) /* use the smallest angle between the edges */
970 angle= fabs(angle-180.0f);
972 if (angle/90.0<=thresh) {
973 EM_select_edge(eed, 1);
976 if (!deselcount) /*have we selected all posible faces?, if so return*/
981 } else if (mode==3) { /* face users */
982 for(eed= em->edges.first; eed; eed= eed->next) {
984 !(eed->f & SELECT) &&
986 base_eed->tmp.l==eed->tmp.l
988 EM_select_edge(eed, 1);
991 if (!deselcount) /*have we selected all posible faces?, if so return*/
995 } else if (mode==4 && base_eed->f2==2) { /* edge angles, f2==2 means the edge has an angle. */
996 for(eed= em->edges.first; eed; eed= eed->next) {
998 !(eed->f & SELECT) &&
1001 (fabs(base_eed->tmp.fp-eed->tmp.fp)<=thresh)
1003 EM_select_edge(eed, 1);
1006 if (!deselcount) /*have we selected all posible faces?, if so return*/
1010 } else if (mode==5) { /* edge crease */
1011 for(eed= em->edges.first; eed; eed= eed->next) {
1013 !(eed->f & SELECT) &&
1015 (fabs(base_eed->crease-eed->crease) <= thresh)
1017 EM_select_edge(eed, 1);
1020 if (!deselcount) /*have we selected all posible faces?, if so return*/
1024 } else if (mode==6) { /* edge seam */
1025 for(eed= em->edges.first; eed; eed= eed->next) {
1027 !(eed->f & SELECT) &&
1029 (eed->seam == base_eed->seam)
1031 EM_select_edge(eed, 1);
1034 if (!deselcount) /*have we selected all posible faces?, if so return*/
1038 } else if (mode==7) { /* edge sharp */
1039 for(eed= em->edges.first; eed; eed= eed->next) {
1041 !(eed->f & SELECT) &&
1043 (eed->sharp == base_eed->sharp)
1045 EM_select_edge(eed, 1);
1048 if (!deselcount) /*have we selected all posible faces?, if so return*/
1057 /* wrap the above function but do selection flushing edge to face */
1058 static int similar_edge_select_exec(bContext *C, wmOperator *op)
1060 Scene *scene= CTX_data_scene(C);
1061 Object *obedit= CTX_data_edit_object(C);
1062 Mesh *me= obedit->data;
1063 EditMesh *em= me->edit_mesh;
1065 int selcount = similar_edge_select__internal(scene, em, RNA_int_get(op->ptr, "type"));
1068 /* here was an edge-mode only select flush case, has to be generalized */
1069 EM_selectmode_flush(em);
1070 WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
1071 return OPERATOR_FINISHED;
1074 return OPERATOR_CANCELLED;
1077 void MESH_OT_similar_edge_select(wmOperatorType *ot)
1080 ot->name= "Similar Edge Select";
1081 ot->idname= "MESH_OT_similar_edge_select";
1084 ot->invoke= WM_menu_invoke;
1085 ot->exec= similar_edge_select_exec;
1086 ot->poll= ED_operator_editmesh;
1089 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1092 RNA_def_enum(ot->srna, "type", prop_simedge_types, 0, "Type", "");
1095 /* ********************************* */
1100 mode 2: same number of face users
1101 mode 3: same vertex groups
1103 static EnumPropertyItem prop_simvertex_types[] = {
1104 {0, "NORMAL", "Normal", ""},
1105 {1, "FACE", "Amount of Vertices in Face", ""},
1106 {2, "VGROUP", "Vertex Groups", ""},
1107 {0, NULL, NULL, NULL}
1111 static int similar_vert_select_exec(bContext *C, wmOperator *op)
1113 Scene *scene= CTX_data_scene(C);
1114 Object *obedit= CTX_data_edit_object(C);
1115 Mesh *me= obedit->data;
1116 EditMesh *em= me->edit_mesh;
1117 EditVert *eve, *base_eve=NULL;
1118 unsigned int selcount=0; /* count how many new edges we select*/
1120 /*count how many visible selected edges there are,
1121 so we can return when there are none left */
1122 unsigned int deselcount=0;
1125 float thresh= scene->toolsettings->select_thresh;
1127 for(eve= em->verts.first; eve; eve= eve->next) {
1129 if (eve->f & SELECT) {
1136 /* set all eve->tmp.l to 0 we use them later.*/
1142 if (!ok || !deselcount) /* no data selected OR no more data to select*/
1145 if(RNA_enum_is_equal(op->ptr, "type", "FACE")) {
1146 /* store face users */
1149 /* count how many faces each edge uses use tmp->l */
1150 for(efa= em->faces.first; efa; efa= efa->next) {
1154 if (efa->v4) efa->v4->tmp.l++;
1159 for(base_eve= em->verts.first; base_eve; base_eve= base_eve->next) {
1162 if(RNA_enum_is_equal(op->ptr, "type", "NORMAL")) {
1164 for(eve= em->verts.first; eve; eve= eve->next) {
1165 if (!(eve->f & SELECT) && !eve->h) {
1166 angle= VecAngle2(base_eve->no, eve->no);
1167 if (angle/180.0<=thresh) {
1171 if (!deselcount) /*have we selected all posible faces?, if so return*/
1177 else if(RNA_enum_is_equal(op->ptr, "type", "FACE")) {
1178 for(eve= em->verts.first; eve; eve= eve->next) {
1180 !(eve->f & SELECT) &&
1182 base_eve->tmp.l==eve->tmp.l
1187 if (!deselcount) /*have we selected all posible faces?, if so return*/
1192 else if(RNA_enum_is_equal(op->ptr, "type", "VGROUP")) {
1193 MDeformVert *dvert, *base_dvert;
1194 short i, j; /* weight index */
1196 base_dvert= CustomData_em_get(&em->vdata, base_eve->data,
1199 if (!base_dvert || base_dvert->totweight == 0)
1202 for(eve= em->verts.first; eve; eve= eve->next) {
1203 dvert= CustomData_em_get(&em->vdata, eve->data,
1206 if (dvert && !(eve->f & SELECT) && !eve->h && dvert->totweight) {
1207 /* do the extra check for selection in the following if, so were not
1208 checking verts that may be alredy selected */
1209 for (i=0; base_dvert->totweight >i && !(eve->f & SELECT); i++) {
1210 for (j=0; dvert->totweight >j; j++) {
1211 if (base_dvert->dw[i].def_nr==dvert->dw[j].def_nr) {
1215 if (!deselcount) /*have we selected all posible faces?, if so return*/
1225 } /* end basevert loop */
1228 WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
1229 return OPERATOR_FINISHED;
1231 return OPERATOR_CANCELLED;
1234 void MESH_OT_similar_vertex_select(wmOperatorType *ot)
1237 ot->name= "Similar Vertex Select";
1238 ot->idname= "MESH_OT_similar_vertex_select";
1241 ot->invoke= WM_menu_invoke;
1242 ot->exec= similar_vert_select_exec;
1243 ot->poll= ED_operator_editmesh;
1246 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1249 RNA_def_enum(ot->srna, "type", prop_simvertex_types, 0, "Type", "");
1252 /* ******************************************* */
1255 int mesh_layers_menu_charlen(CustomData *data, int type)
1258 /* see if there is a duplicate */
1259 for(i=0; i<data->totlayer; i++) {
1260 if((&data->layers[i])->type == type) {
1261 /* we could count the chars here but we'll just assumeme each
1262 * is 32 chars with some room for the menu text - 40 should be fine */
1269 /* this function adds menu text into an existing string.
1270 * this string's size should be allocated with mesh_layers_menu_charlen */
1271 void mesh_layers_menu_concat(CustomData *data, int type, char *str)
1275 CustomDataLayer *layer;
1277 /* see if there is a duplicate */
1278 for(i=0; i<data->totlayer; i++) {
1279 layer = &data->layers[i];
1280 if(layer->type == type) {
1281 str_pt += sprintf(str_pt, "%s%%x%d|", layer->name, count);
1287 int mesh_layers_menu(CustomData *data, int type) {
1291 str_pt = str = MEM_mallocN(mesh_layers_menu_charlen(data, type) + 18, "layer menu");
1294 str_pt += sprintf(str_pt, "Layers%%t|");
1296 mesh_layers_menu_concat(data, type, str_pt);
1303 void EM_mesh_copy_edge(EditMesh *em, short type)
1308 EditEdge *eed, *eed_act;
1309 float vec[3], vec_mid[3], eed_len, eed_len_act;
1313 ese = em->selected.last;
1316 eed_act = (EditEdge*)ese->data;
1319 case 1: /* copy crease */
1320 for(eed=em->edges.first; eed; eed=eed->next) {
1321 if (eed->f & SELECT && eed != eed_act && eed->crease != eed_act->crease) {
1322 eed->crease = eed_act->crease;
1327 case 2: /* copy bevel weight */
1328 for(eed=em->edges.first; eed; eed=eed->next) {
1329 if (eed->f & SELECT && eed != eed_act && eed->bweight != eed_act->bweight) {
1330 eed->bweight = eed_act->bweight;
1336 case 3: /* copy length */
1337 eed_len_act = VecLenf(eed_act->v1->co, eed_act->v2->co);
1338 for(eed=em->edges.first; eed; eed=eed->next) {
1339 if (eed->f & SELECT && eed != eed_act) {
1341 eed_len = VecLenf(eed->v1->co, eed->v2->co);
1343 if (eed_len == eed_len_act) continue;
1344 /* if this edge is zero length we cont do anything with it*/
1345 if (eed_len == 0.0f) continue;
1346 if (eed_len_act == 0.0f) {
1347 VecAddf(vec_mid, eed->v1->co, eed->v2->co);
1348 VecMulf(vec_mid, 0.5);
1349 VECCOPY(eed->v1->co, vec_mid);
1350 VECCOPY(eed->v2->co, vec_mid);
1352 /* copy the edge length */
1353 VecAddf(vec_mid, eed->v1->co, eed->v2->co);
1354 VecMulf(vec_mid, 0.5);
1357 VecSubf(vec, eed->v1->co, vec_mid);
1358 VecMulf(vec, eed_len_act/eed_len);
1359 VecAddf(eed->v1->co, vec, vec_mid);
1362 VecSubf(vec, eed->v2->co, vec_mid);
1363 VecMulf(vec, eed_len_act/eed_len);
1364 VecAddf(eed->v2->co, vec, vec_mid);
1371 recalc_editnormals(em);
1377 // DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
1382 void EM_mesh_copy_face(EditMesh *em, short type)
1386 EditFace *efa, *efa_act;
1387 MTFace *tf, *tf_act = NULL;
1388 MCol *mcol, *mcol_act = NULL;
1390 efa_act = EM_get_actFace(em, 0);
1392 if (!efa_act) return;
1394 tf_act = CustomData_em_get(&em->fdata, efa_act->data, CD_MTFACE);
1395 mcol_act = CustomData_em_get(&em->fdata, efa_act->data, CD_MCOL);
1398 case 1: /* copy material */
1399 for(efa=em->faces.first; efa; efa=efa->next) {
1400 if (efa->f & SELECT && efa->mat_nr != efa_act->mat_nr) {
1401 efa->mat_nr = efa_act->mat_nr;
1406 case 2: /* copy image */
1408 error("mesh has no uv/image layers");
1411 for(efa=em->faces.first; efa; efa=efa->next) {
1412 if (efa->f & SELECT && efa != efa_act) {
1413 tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
1414 if (tf_act->tpage) {
1415 tf->tpage = tf_act->tpage;
1419 tf->mode &= ~TF_TEX;
1421 tf->tile= tf_act->tile;
1427 case 3: /* copy UV's */
1429 error("mesh has no uv/image layers");
1432 for(efa=em->faces.first; efa; efa=efa->next) {
1433 if (efa->f & SELECT && efa != efa_act) {
1434 tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
1435 memcpy(tf->uv, tf_act->uv, sizeof(tf->uv));
1440 case 4: /* mode's */
1442 error("mesh has no uv/image layers");
1445 for(efa=em->faces.first; efa; efa=efa->next) {
1446 if (efa->f & SELECT && efa != efa_act) {
1447 tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
1448 tf->mode= tf_act->mode;
1453 case 5: /* copy transp's */
1455 error("mesh has no uv/image layers");
1458 for(efa=em->faces.first; efa; efa=efa->next) {
1459 if (efa->f & SELECT && efa != efa_act) {
1460 tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
1461 tf->transp= tf_act->transp;
1467 case 6: /* copy vcols's */
1469 error("mesh has no color layers");
1472 /* guess the 4th color if needs be */
1476 /* guess the othe vale, we may need to use it
1478 * Modifying the 4th value of the mcol is ok here since its not seen
1481 val = ((float)(mcol_act->r + (mcol_act+1)->r + (mcol_act+2)->r)) / 3; CLAMP(val, 0, 255);
1482 (mcol_act+3)->r = (char)val;
1484 val = ((float)(mcol_act->g + (mcol_act+1)->g + (mcol_act+2)->g)) / 3; CLAMP(val, 0, 255);
1485 (mcol_act+3)->g = (char)val;
1487 val = ((float)(mcol_act->b + (mcol_act+1)->b + (mcol_act+2)->b)) / 3; CLAMP(val, 0, 255);
1488 (mcol_act+3)->b = (char)val;
1492 for(efa=em->faces.first; efa; efa=efa->next) {
1493 if (efa->f & SELECT && efa != efa_act) {
1494 /* TODO - make copy from tri to quad guess the 4th vert */
1495 mcol = CustomData_em_get(&em->fdata, efa->data, CD_MCOL);
1496 memcpy(mcol, mcol_act, sizeof(MCol)*4);
1505 // DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
1511 void EM_mesh_copy_face_layer(EditMesh *em, short type)
1516 MTFace *tf, *tf_from;
1517 MCol *mcol, *mcol_from;
1525 if (CustomData_number_of_layers(&em->fdata, CD_MTFACE)<2) {
1526 error("mesh does not have multiple uv/image layers");
1529 int layer_orig_idx, layer_idx;
1531 layer_idx = mesh_layers_menu(&em->fdata, CD_MTFACE);
1532 if (layer_idx<0) return;
1534 /* warning, have not updated mesh pointers however this is not needed since we swicth back */
1535 layer_orig_idx = CustomData_get_active_layer(&em->fdata, CD_MTFACE);
1536 if (layer_idx==layer_orig_idx)
1539 /* get the tfaces */
1540 CustomData_set_layer_active(&em->fdata, CD_MTFACE, (int)layer_idx);
1541 /* store the tfaces in our temp */
1542 for(efa=em->faces.first; efa; efa=efa->next) {
1543 if (efa->f & SELECT) {
1544 efa->tmp.p = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
1547 CustomData_set_layer_active(&em->fdata, CD_MTFACE, layer_orig_idx);
1551 case 10: /* select vcol layers - make sure this stays in sync with above code */
1552 if (CustomData_number_of_layers(&em->fdata, CD_MCOL)<2) {
1553 error("mesh does not have multiple color layers");
1556 int layer_orig_idx, layer_idx;
1558 layer_idx = mesh_layers_menu(&em->fdata, CD_MCOL);
1559 if (layer_idx<0) return;
1561 /* warning, have not updated mesh pointers however this is not needed since we swicth back */
1562 layer_orig_idx = CustomData_get_active_layer(&em->fdata, CD_MCOL);
1563 if (layer_idx==layer_orig_idx)
1566 /* get the tfaces */
1567 CustomData_set_layer_active(&em->fdata, CD_MCOL, (int)layer_idx);
1568 /* store the tfaces in our temp */
1569 for(efa=em->faces.first; efa; efa=efa->next) {
1570 if (efa->f & SELECT) {
1571 efa->tmp.p = CustomData_em_get(&em->fdata, efa->data, CD_MCOL);
1574 CustomData_set_layer_active(&em->fdata, CD_MCOL, layer_orig_idx);
1580 /* layer copy only - sanity checks done above */
1582 case 7: /* copy UV's only */
1583 for(efa=em->faces.first; efa; efa=efa->next) {
1584 if (efa->f & SELECT) {
1585 tf_from = (MTFace *)efa->tmp.p; /* not active but easier to use this way */
1586 tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
1587 memcpy(tf->uv, tf_from->uv, sizeof(tf->uv));
1592 case 8: /* copy image settings only */
1593 for(efa=em->faces.first; efa; efa=efa->next) {
1594 if (efa->f & SELECT) {
1595 tf_from = (MTFace *)efa->tmp.p; /* not active but easier to use this way */
1596 tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
1597 if (tf_from->tpage) {
1598 tf->tpage = tf_from->tpage;
1602 tf->mode &= ~TF_TEX;
1604 tf->tile= tf_from->tile;
1609 case 9: /* copy all tface info */
1610 for(efa=em->faces.first; efa; efa=efa->next) {
1611 if (efa->f & SELECT) {
1612 tf_from = (MTFace *)efa->tmp.p; /* not active but easier to use this way */
1613 tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
1614 memcpy(tf->uv, ((MTFace *)efa->tmp.p)->uv, sizeof(tf->uv));
1615 tf->tpage = tf_from->tpage;
1616 tf->mode = tf_from->mode;
1617 tf->transp = tf_from->transp;
1623 for(efa=em->faces.first; efa; efa=efa->next) {
1624 if (efa->f & SELECT) {
1625 mcol_from = (MCol *)efa->tmp.p;
1626 mcol = CustomData_em_get(&em->fdata, efa->data, CD_MCOL);
1627 memcpy(mcol, mcol_from, sizeof(MCol)*4);
1635 // DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
1641 /* ctrl+c in mesh editmode */
1642 void mesh_copy_menu(EditMesh *em)
1648 ese = em->selected.last;
1650 /* Faces can have a NULL ese, so dont return on a NULL ese here */
1652 if(ese && ese->type == EDITVERT) {
1653 /* EditVert *ev, *ev_act = (EditVert*)ese->data;
1654 ret= pupmenu(""); */
1655 } else if(ese && ese->type == EDITEDGE) {
1656 ret= pupmenu("Copy Active Edge to Selected%t|Crease%x1|Bevel Weight%x2|Length%x3");
1659 EM_mesh_copy_edge(em, ret);
1661 } else if(ese==NULL || ese->type == EDITFACE) {
1663 "Copy Face Selected%t|"
1664 "Active Material%x1|Active Image%x2|Active UV Coords%x3|"
1665 "Active Mode%x4|Active Transp%x5|Active Vertex Colors%x6|%l|"
1667 "TexFace UVs from layer%x7|"
1668 "TexFace Images from layer%x8|"
1669 "TexFace All from layer%x9|"
1670 "Vertex Colors from layer%x10");
1674 EM_mesh_copy_face(em, ret);
1676 EM_mesh_copy_face_layer(em, ret);
1682 /* **************** LOOP SELECTS *************** */
1684 /* selects quads in loop direction of indicated edge */
1685 /* only flush over edges with valence <= 2 */
1686 void faceloop_select(EditMesh *em, EditEdge *startedge, int select)
1692 /* in eed->f1 we put the valence (amount of faces in edge) */
1693 /* in eed->f2 we put tagged flag as correct loop */
1694 /* in efa->f1 we put tagged flag as correct to select */
1696 for(eed= em->edges.first; eed; eed= eed->next) {
1700 for(efa= em->faces.first; efa; efa= efa->next) {
1706 if(efa->e4) efa->e4->f1++;
1710 /* tag startedge OK*/
1716 for(efa= em->faces.first; efa; efa= efa->next) {
1717 if(efa->h==0 && efa->e4 && efa->f1==0) { /* not done quad */
1718 if(efa->e1->f1<=2 && efa->e2->f1<=2 && efa->e3->f1<=2 && efa->e4->f1<=2) { /* valence ok */
1720 /* if edge tagged, select opposing edge and mark face ok */
1726 else if(efa->e2->f2) {
1746 /* (de)select the faces */
1748 for(efa= em->faces.first; efa; efa= efa->next) {
1749 if(efa->f1) EM_select_face(efa, select);
1755 /* helper for edgeloop_select, checks for eed->f2 tag in faces */
1756 static int edge_not_in_tagged_face(EditMesh *em, EditEdge *eed)
1760 for(efa= em->faces.first; efa; efa= efa->next) {
1762 if(efa->e1==eed || efa->e2==eed || efa->e3==eed || efa->e4==eed) { /* edge is in face */
1763 if(efa->e1->f2 || efa->e2->f2 || efa->e3->f2 || (efa->e4 && efa->e4->f2)) { /* face is tagged */
1772 /* selects or deselects edges that:
1773 - if edges has 2 faces:
1774 - has vertices with valence of 4
1775 - not shares face with previous edge
1776 - if edge has 1 face:
1777 - has vertices with valence 4
1778 - not shares face with previous edge
1779 - but also only 1 face
1781 - has vertices with valence 2
1783 static void edgeloop_select(EditMesh *em, EditEdge *starteed, int select)
1790 /* in f1 we put the valence (amount of edges in a vertex, or faces in edge) */
1791 /* in eed->f2 and efa->f1 we put tagged flag as correct loop */
1792 for(eve= em->verts.first; eve; eve= eve->next) {
1796 for(eed= em->edges.first; eed; eed= eed->next) {
1799 if((eed->h & 1)==0) { /* fgon edges add to valence too */
1800 eed->v1->f1++; eed->v2->f1++;
1803 for(efa= em->faces.first; efa; efa= efa->next) {
1809 if(efa->e4) efa->e4->f1++;
1813 /* looped edges & vertices get tagged f2 */
1815 if(starteed->v1->f1<5) starteed->v1->f2= 1;
1816 if(starteed->v2->f1<5) starteed->v2->f2= 1;
1817 /* sorry, first edge isnt even ok */
1818 if(starteed->v1->f2==0 && starteed->v2->f2==0) looking= 0;
1823 /* find correct valence edges which are not tagged yet, but connect to tagged one */
1824 for(eed= em->edges.first; eed; eed= eed->next) {
1825 if(eed->h==0 && eed->f2==0) { /* edge not hidden, not tagged */
1826 if( (eed->v1->f1<5 && eed->v1->f2) || (eed->v2->f1<5 && eed->v2->f2)) { /* valence of vertex OK, and is tagged */
1827 /* new edge is not allowed to be in face with tagged edge */
1828 if(edge_not_in_tagged_face(em, eed)) {
1829 if(eed->f1==starteed->f1) { /* same amount of faces */
1832 if(eed->v2->f1<5) eed->v2->f2= 1;
1833 if(eed->v1->f1<5) eed->v1->f2= 1;
1840 /* and we do the select */
1841 for(eed= em->edges.first; eed; eed= eed->next) {
1842 if(eed->f2) EM_select_edge(eed, select);
1847 Almostly exactly the same code as faceloop select
1849 static void edgering_select(EditMesh *em, EditEdge *startedge, int select)
1855 /* in eed->f1 we put the valence (amount of faces in edge) */
1856 /* in eed->f2 we put tagged flag as correct loop */
1857 /* in efa->f1 we put tagged flag as correct to select */
1859 for(eed= em->edges.first; eed; eed= eed->next) {
1863 for(efa= em->faces.first; efa; efa= efa->next) {
1869 if(efa->e4) efa->e4->f1++;
1873 /* tag startedge OK */
1879 for(efa= em->faces.first; efa; efa= efa->next) {
1880 if(efa->e4 && efa->f1==0 && !efa->h) { /* not done quad */
1881 if(efa->e1->f1<=2 && efa->e2->f1<=2 && efa->e3->f1<=2 && efa->e4->f1<=2) { /* valence ok */
1883 /* if edge tagged, select opposing edge and mark face ok */
1889 else if(efa->e2->f2) {
1909 /* (de)select the edges */
1910 for(eed= em->edges.first; eed; eed= eed->next) {
1911 if(eed->f2) EM_select_edge(eed, select);
1915 void loop_multiselect(EditMesh *em, int looptype)
1919 int edindex, edfirstcount;
1921 /* sets em->totedgesel */
1922 EM_nedges_selected(em);
1924 edarray = MEM_mallocN(sizeof(EditEdge*)*em->totedgesel,"edge array");
1926 edfirstcount = em->totedgesel;
1928 for(eed=em->edges.first; eed; eed=eed->next){
1930 edarray[edindex] = eed;
1936 for(edindex = 0; edindex < edfirstcount; edindex +=1){
1937 eed = edarray[edindex];
1938 edgering_select(em, eed,SELECT);
1940 EM_selectmode_flush(em);
1943 for(edindex = 0; edindex < edfirstcount; edindex +=1){
1944 eed = edarray[edindex];
1945 edgeloop_select(em, eed,SELECT);
1947 EM_selectmode_flush(em);
1950 // if (EM_texFaceCheck())
1953 /* ***************** MAIN MOUSE SELECTION ************** */
1956 /* ***************** loop select (non modal) ************** */
1958 static void mouse_mesh_loop(bContext *C, short mval[2], short extend, short ring)
1966 em_setup_viewcontext(C, &vc);
1967 vc.mval[0]= mval[0];
1968 vc.mval[1]= mval[1];
1971 eed= findnearestedge(&vc, &dist);
1973 if(extend==0) EM_clear_flag_all(em, SELECT);
1975 if((eed->f & SELECT)==0) select=1;
1976 else if(extend) select=0;
1978 if(em->selectmode & SCE_SELECT_FACE) {
1979 faceloop_select(em, eed, select);
1981 else if(em->selectmode & SCE_SELECT_EDGE) {
1983 edgering_select(em, eed, select);
1985 edgeloop_select(em, eed, select);
1987 else if(em->selectmode & SCE_SELECT_VERTEX) {
1989 edgering_select(em, eed, select);
1991 edgeloop_select(em, eed, select);
1994 EM_selectmode_flush(em);
1995 // if (EM_texFaceCheck())
1997 WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, vc.obedit);
2001 static int mesh_loop_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
2003 ARegion *ar= CTX_wm_region(C);
2006 mval[0]= event->x - ar->winrct.xmin;
2007 mval[1]= event->y - ar->winrct.ymin;
2009 view3d_operator_needs_opengl(C);
2011 mouse_mesh_loop(C, mval, RNA_boolean_get(op->ptr, "extend"),
2012 RNA_boolean_get(op->ptr, "ring"));
2014 /* cannot do tweaks for as long this keymap is after transform map */
2015 return OPERATOR_FINISHED;
2018 void MESH_OT_loop_select(wmOperatorType *ot)
2021 ot->name= "Loop Select";
2022 ot->idname= "MESH_OT_loop_select";
2025 ot->invoke= mesh_loop_select_invoke;
2026 ot->poll= ED_operator_editmesh;
2029 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2032 RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "");
2033 RNA_def_boolean(ot->srna, "ring", 0, "Select Ring", "");
2036 /* ******************* mesh shortest path select, uses prev-selected edge ****************** */
2038 /* since you want to create paths with multiple selects, it doesn't have extend option */
2039 static void mouse_mesh_shortest_path(bContext *C, short mval[2])
2046 em_setup_viewcontext(C, &vc);
2047 vc.mval[0]= mval[0];
2048 vc.mval[1]= mval[1];
2051 eed= findnearestedge(&vc, &dist);
2053 Mesh *me= vc.obedit->data;
2056 if (em->selected.last) {
2057 EditSelection *ese = em->selected.last;
2059 if(ese && ese->type == EDITEDGE) {
2061 eed_act = (EditEdge*)ese->data;
2062 if (eed_act != eed) {
2063 if (edgetag_shortest_path(vc.scene, em, eed_act, eed)) {
2064 EM_remove_selection(em, eed_act, EDITEDGE);
2071 int act = (edgetag_context_check(vc.scene, eed)==0);
2072 edgetag_context_set(vc.scene, eed, act); /* switch the edge option */
2075 EM_selectmode_flush(em);
2077 /* even if this is selected it may not be in the selection list */
2078 if(edgetag_context_check(vc.scene, eed)==0)
2079 EM_remove_selection(em, eed, EDITEDGE);
2081 EM_store_selection(em, eed, EDITEDGE);
2083 /* force drawmode for mesh */
2084 switch (vc.scene->toolsettings->edge_mode) {
2086 case EDGE_MODE_TAG_SEAM:
2087 me->drawflag |= ME_DRAWSEAMS;
2089 case EDGE_MODE_TAG_SHARP:
2090 me->drawflag |= ME_DRAWSHARP;
2092 case EDGE_MODE_TAG_CREASE:
2093 me->drawflag |= ME_DRAWCREASES;
2095 case EDGE_MODE_TAG_BEVEL:
2096 me->drawflag |= ME_DRAWBWEIGHTS;
2100 DAG_object_flush_update(vc.scene, vc.obedit, OB_RECALC_DATA);
2102 WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, vc.obedit);
2107 static int mesh_shortest_path_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
2109 ARegion *ar= CTX_wm_region(C);
2112 mval[0]= event->x - ar->winrct.xmin;
2113 mval[1]= event->y - ar->winrct.ymin;
2115 view3d_operator_needs_opengl(C);
2117 mouse_mesh_shortest_path(C, mval);
2119 return OPERATOR_FINISHED;
2122 void MESH_OT_shortest_path_select(wmOperatorType *ot)
2125 ot->name= "Shortest Path Select";
2126 ot->idname= "MESH_OT_shortest_path_select";
2129 ot->invoke= mesh_shortest_path_select_invoke;
2130 ot->poll= ED_operator_editmesh;
2133 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2136 RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "");
2140 /* ************************************************** */
2143 /* here actual select happens */
2144 /* gets called via generic mouse select operator */
2145 void mouse_mesh(bContext *C, short mval[2], short extend)
2152 /* setup view context for argument to callbacks */
2153 em_setup_viewcontext(C, &vc);
2154 vc.mval[0]= mval[0];
2155 vc.mval[1]= mval[1];
2157 if(unified_findnearest(&vc, &eve, &eed, &efa)) {
2159 if(extend==0) EM_clear_flag_all(vc.em, SELECT);
2162 /* set the last selected face */
2163 EM_set_actFace(vc.em, efa);
2165 if( (efa->f & SELECT)==0 ) {
2166 EM_store_selection(vc.em, efa, EDITFACE);
2167 EM_select_face_fgon(vc.em, efa, 1);
2170 EM_remove_selection(vc.em, efa, EDITFACE);
2171 EM_select_face_fgon(vc.em, efa, 0);
2175 if((eed->f & SELECT)==0) {
2176 EM_store_selection(vc.em, eed, EDITEDGE);
2177 EM_select_edge(eed, 1);
2180 EM_remove_selection(vc.em, eed, EDITEDGE);
2181 EM_select_edge(eed, 0);
2185 if((eve->f & SELECT)==0) {
2187 EM_store_selection(vc.em, eve, EDITVERT);
2190 EM_remove_selection(vc.em, eve, EDITVERT);
2195 EM_selectmode_flush(vc.em);
2197 // if (EM_texFaceCheck()) {
2199 if (efa && efa->mat_nr != vc.obedit->actcol-1) {
2200 vc.obedit->actcol= efa->mat_nr+1;
2201 vc.em->mat_nr= efa->mat_nr;
2202 // BIF_preview_changed(ID_MA);
2206 WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, vc.obedit);
2210 // XXX should we use CTX_scene(C)->selectmode & SCE_SELECT_FACE like it was in the past ? calls selectconnected_delimit_mesh_all if true
2211 void selectconnected_mesh_all(EditMesh *em)
2215 short done=1, toggle=0;
2217 if(em->edges.first==0) return;
2223 if(toggle & 1) eed= em->edges.first;
2224 else eed= em->edges.last;
2230 if(v1->f & SELECT) {
2231 if( (v2->f & SELECT)==0 ) {
2236 else if(v2->f & SELECT) {
2237 if( (v1->f & SELECT)==0 ) {
2243 if(toggle & 1) eed= eed->next;
2244 else eed= eed->prev;
2248 /* now use vertex select flag to select rest */
2249 EM_select_flush(em);
2251 // if (EM_texFaceCheck())
2254 static int select_linked_exec(bContext *C, wmOperator *op)
2256 Object *obedit= CTX_data_edit_object(C);
2257 EditMesh *em= ((Mesh *)obedit->data)->edit_mesh;
2259 selectconnected_mesh_all(em);
2261 WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
2262 return OPERATOR_FINISHED;
2265 void MESH_OT_select_linked(wmOperatorType *ot)
2268 ot->name= "Select Linked All";
2269 ot->idname= "MESH_OT_select_linked";
2272 ot->exec= select_linked_exec;
2273 ot->poll= ED_operator_editmesh;
2276 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2280 /* *********** select connected ************* */
2282 // XXX should we use CTX_scene(C)->selectmode & SCE_SELECT_FACE like it was in the past ? calls selectconnected_delimit_mesh if true
2283 static int select_linked_pick_invoke(bContext *C, wmOperator *op, wmEvent *event)
2285 Object *obedit= CTX_data_edit_object(C);
2287 EditVert *eve, *v1, *v2;
2290 short done=1, toggle=0;
2291 int sel= !RNA_boolean_get(op->ptr, "deselect");
2293 /* unified_finednearest needs ogl */
2294 view3d_operator_needs_opengl(C);
2296 /* setup view context for argument to callbacks */
2297 em_setup_viewcontext(C, &vc);
2299 if(vc.em->edges.first==0) return OPERATOR_CANCELLED;
2301 vc.mval[0]= event->x - vc.ar->winrct.xmin;
2302 vc.mval[1]= event->y - vc.ar->winrct.ymin;
2304 if( unified_findnearest(&vc, &eve, &eed, &efa)==0 ) {
2305 return OPERATOR_CANCELLED;
2308 /* clear test flags */
2309 for(v1= vc.em->verts.first; v1; v1= v1->next) v1->f1= 0;
2311 /* start vertex/face/edge */
2313 else if(eed) eed->v1->f1= eed->v2->f1= 1;
2314 else efa->v1->f1= efa->v2->f1= efa->v3->f1= 1;
2316 /* set flag f1 if affected */
2321 if(toggle & 1) eed= vc.em->edges.first;
2322 else eed= vc.em->edges.last;
2329 if(v1->f1 && v2->f1==0) {
2333 else if(v1->f1==0 && v2->f1) {
2339 if(toggle & 1) eed= eed->next;
2340 else eed= eed->prev;
2344 /* now use vertex f1 flag to select/deselect */
2345 for(eed= vc.em->edges.first; eed; eed= eed->next) {
2346 if(eed->v1->f1 && eed->v2->f1)
2347 EM_select_edge(eed, sel);
2349 for(efa= vc.em->faces.first; efa; efa= efa->next) {
2350 if(efa->v1->f1 && efa->v2->f1 && efa->v3->f1 && (efa->v4==NULL || efa->v4->f1))
2351 EM_select_face(efa, sel);
2353 /* no flush needed, connected geometry is done */
2355 // if (EM_texFaceCheck())
2357 WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
2358 return OPERATOR_FINISHED;
2361 void MESH_OT_select_linked_pick(wmOperatorType *ot)
2364 ot->name= "Select Linked";
2365 ot->idname= "MESH_OT_select_linked_pick";
2368 ot->invoke= select_linked_pick_invoke;
2369 ot->poll= ED_operator_editmesh;
2372 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2374 RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "");
2377 /* ************************* */
2379 /* for use with selectconnected_delimit_mesh only! */
2380 #define is_edge_delimit_ok(eed) ((eed->tmp.l == 1) && (eed->seam==0))
2381 #define is_face_tag(efa) is_edge_delimit_ok(efa->e1) || is_edge_delimit_ok(efa->e2) || is_edge_delimit_ok(efa->e3) || (efa->v4 && is_edge_delimit_ok(efa->e4))
2383 #define face_tag(efa)\
2384 if(efa->v4) efa->tmp.l= efa->e1->tmp.l= efa->e2->tmp.l= efa->e3->tmp.l= efa->e4->tmp.l= 1;\
2385 else efa->tmp.l= efa->e1->tmp.l= efa->e2->tmp.l= efa->e3->tmp.l= 1;
2387 /* all - 1) use all faces for extending the selection 2) only use the mouse face
2388 * sel - 1) select 0) deselect
2390 static void selectconnected_delimit_mesh__internal(ViewContext *vc, short all, short sel)
2392 EditMesh *em= vc->em;
2395 short done=1, change=0;
2398 if(em->faces.first==0) return;
2400 /* flag all edges as off*/
2401 for(eed= em->edges.first; eed; eed= eed->next)
2405 for(efa= em->faces.first; efa; efa= efa->next) {
2406 if (efa->f & SELECT) {
2413 EditFace *efa_mouse = findnearestface(vc, &dist);
2416 /* error("Nothing indicated "); */ /* this is mostly annoying, eps with occluded geometry */
2420 for(efa= em->faces.first; efa; efa= efa->next) {
2423 efa_mouse->tmp.l = 1;
2424 face_tag(efa_mouse);
2429 /* simple algo - select all faces that have a selected edge
2430 * this intern selects the edge, repeat until nothing is left to do */
2431 for(efa= em->faces.first; efa; efa= efa->next) {
2432 if ((efa->tmp.l == 0) && (!efa->h)) {
2433 if (is_face_tag(efa)) {
2441 for(efa= em->faces.first; efa; efa= efa->next) {
2444 if (!(efa->f & SELECT)) {
2445 EM_select_face(efa, 1);
2449 if (efa->f & SELECT) {
2450 EM_select_face(efa, 0);
2460 if (!sel) /* make sure de-selecting faces didnt de-select the verts/edges connected to selected faces, this is common with boundries */
2461 for(efa= em->faces.first; efa; efa= efa->next)
2462 if (efa->f & SELECT)
2463 EM_select_face(efa, 1);
2465 // if (EM_texFaceCheck())
2469 #undef is_edge_delimit_ok
2473 void selectconnected_delimit_mesh(EditMesh *em)
2476 // XXX selectconnected_delimit_mesh__internal(em, 0, ((G.qual & LR_SHIFTKEY)==0));
2478 void selectconnected_delimit_mesh_all(ViewContext *vc)
2480 selectconnected_delimit_mesh__internal(vc, 1, 1);
2484 /* swap is 0 or 1, if 1 it hides not selected */
2485 static void hide_mesh(EditMesh *em, int swap)
2492 if(em==NULL) return;
2494 /* hide happens on least dominant select mode, and flushes up, not down! (helps preventing errors in subsurf) */
2495 /* - vertex hidden, always means edge is hidden too
2496 - edge hidden, always means face is hidden too
2497 - face hidden, only set face hide
2498 - then only flush back down what's absolute hidden
2500 if(em->selectmode & SCE_SELECT_VERTEX) {
2501 for(eve= em->verts.first; eve; eve= eve->next) {
2502 if((eve->f & SELECT)!=swap) {
2508 for(eed= em->edges.first; eed; eed= eed->next) {
2509 if(eed->v1->h || eed->v2->h) {
2515 for(efa= em->faces.first; efa; efa= efa->next) {
2516 if(efa->e1->h & 1 || efa->e2->h & 1 || efa->e3->h & 1 || (efa->e4 && efa->e4->h & 1)) {
2522 else if(em->selectmode & SCE_SELECT_EDGE) {
2524 for(eed= em->edges.first; eed; eed= eed->next) {
2525 if((eed->f & SELECT)!=swap) {
2527 EM_select_edge(eed, 0);
2531 for(efa= em->faces.first; efa; efa= efa->next) {
2532 if(efa->e1->h & 1 || efa->e2->h & 1 || efa->e3->h & 1 || (efa->e4 && efa->e4->h & 1)) {
2540 for(efa= em->faces.first; efa; efa= efa->next) {
2541 if((efa->f & SELECT)!=swap) {
2543 EM_select_face(efa, 0);
2548 /* flush down, only whats 100% hidden */
2549 for(eve= em->verts.first; eve; eve= eve->next) eve->f1= 0;
2550 for(eed= em->edges.first; eed; eed= eed->next) eed->f1= 0;
2552 if(em->selectmode & SCE_SELECT_FACE) {
2553 for(efa= em->faces.first; efa; efa= efa->next) {
2554 if(efa->h) a= 1; else a= 2;
2558 if(efa->e4) efa->e4->f1 |= a;
2559 /* When edges are not delt with in their own loop, we need to explicitly re-selct select edges that are joined to unselected faces */
2560 if (swap && (em->selectmode == SCE_SELECT_FACE) && (efa->f & SELECT)) {
2561 EM_select_face(efa, 1);
2566 if(em->selectmode >= SCE_SELECT_EDGE) {
2567 for(eed= em->edges.first; eed; eed= eed->next) {
2568 if(eed->f1==1) eed->h |= 1;
2569 if(eed->h & 1) a= 1; else a= 2;
2575 if(em->selectmode >= SCE_SELECT_VERTEX) {
2576 for(eve= em->verts.first; eve; eve= eve->next) {
2577 if(eve->f1==1) eve->h= 1;
2581 em->totedgesel= em->totfacesel= em->totvertsel= 0;
2582 // if(EM_texFaceCheck())
2584 // DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
2587 static int hide_mesh_exec(bContext *C, wmOperator *op)
2589 Object *obedit= CTX_data_edit_object(C);
2590 EditMesh *em= ((Mesh *)obedit->data)->edit_mesh;
2592 hide_mesh(em, RNA_boolean_get(op->ptr, "invert"));
2594 WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
2595 return OPERATOR_FINISHED;
2598 void MESH_OT_hide(wmOperatorType *ot)
2601 ot->name= "Hide Selection";
2602 ot->idname= "MESH_OT_hide";
2605 ot->exec= hide_mesh_exec;
2606 ot->poll= ED_operator_editmesh;
2609 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2612 RNA_def_boolean(ot->srna, "invert", 0, "Invert", "");
2615 void reveal_mesh(EditMesh *em)
2621 if(em==NULL) return;
2623 for(eve= em->verts.first; eve; eve= eve->next) {
2629 for(eed= em->edges.first; eed; eed= eed->next) {
2632 if(em->selectmode & SCE_SELECT_VERTEX);
2633 else EM_select_edge(eed, 1);
2636 for(efa= em->faces.first; efa; efa= efa->next) {
2639 if(em->selectmode & (SCE_SELECT_EDGE|SCE_SELECT_VERTEX));
2640 else EM_select_face(efa, 1);
2644 EM_fgon_flags(em); /* redo flags and indices for fgons */
2645 EM_selectmode_flush(em);
2647 // if (EM_texFaceCheck())
2648 // DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
2651 static int reveal_mesh_exec(bContext *C, wmOperator *op)
2653 Object *obedit= CTX_data_edit_object(C);
2654 EditMesh *em= ((Mesh *)obedit->data)->edit_mesh;
2658 WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
2659 return OPERATOR_FINISHED;
2662 void MESH_OT_reveal(wmOperatorType *ot)
2665 ot->name= "Reveal Hidden";
2666 ot->idname= "MESH_OT_reveal";
2669 ot->exec= reveal_mesh_exec;
2670 ot->poll= ED_operator_editmesh;
2673 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2676 void hide_tface_uv(EditMesh *em, int swap)
2679 /* no space image here */
2683 if( is_uv_tface_editing_allowed()==0 ) return;
2685 /* call the mesh function if we are in mesh sync sel */
2686 if (G.sima->flag & SI_SYNC_UVSEL) {
2692 for (efa= em->faces.first; efa; efa= efa->next) {
2693 if(efa->f & SELECT) {
2694 tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
2695 if (G.sima->flag & SI_SELACTFACE) {
2696 /* Pretend face mode */
2697 if (( (efa->v4==NULL &&
2698 ( tface->flag & (TF_SEL1|TF_SEL2|TF_SEL3)) == (TF_SEL1|TF_SEL2|TF_SEL3) ) ||
2699 ( tface->flag & (TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4)) == (TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4) ) == 0) {
2701 if (em->selectmode == SCE_SELECT_FACE) {
2703 /* must re-select after */
2704 efa->e1->f &= ~SELECT;
2705 efa->e2->f &= ~SELECT;
2706 efa->e3->f &= ~SELECT;
2707 if(efa->e4) efa->e4->f &= ~SELECT;
2709 EM_select_face(efa, 0);
2712 tface->flag &= ~(TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4);
2713 } else if (em->selectmode == SCE_SELECT_FACE) {
2714 if((tface->flag & (TF_SEL1|TF_SEL2|TF_SEL3))==0) {
2716 EM_select_face(efa, 0);
2717 else if(!(tface->flag & TF_SEL4))
2718 EM_select_face(efa, 0);
2719 tface->flag &= ~(TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4);
2722 /* EM_deselect_flush will deselect the face */
2723 if((tface->flag & TF_SEL1)==0) efa->v1->f &= ~SELECT;
2724 if((tface->flag & TF_SEL2)==0) efa->v2->f &= ~SELECT;
2725 if((tface->flag & TF_SEL3)==0) efa->v3->f &= ~SELECT;
2726 if((efa->v4) && (tface->flag & TF_SEL4)==0) efa->v4->f &= ~SELECT;
2727 tface->flag &= ~(TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4);
2732 for (efa= em->faces.first; efa; efa= efa->next) {
2733 if(efa->f & SELECT) {
2734 tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
2735 if (G.sima->flag & SI_SELACTFACE) {
2736 if ( (efa->v4==NULL &&
2737 ( tface->flag & (TF_SEL1|TF_SEL2|TF_SEL3)) == (TF_SEL1|TF_SEL2|TF_SEL3) ) ||
2738 ( tface->flag & (TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4)) == (TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4) ) {
2740 if (em->selectmode == SCE_SELECT_FACE) {
2742 /* must re-select after */
2743 efa->e1->f &= ~SELECT;
2744 efa->e2->f &= ~SELECT;
2745 efa->e3->f &= ~SELECT;
2746 if(efa->e4) efa->e4->f &= ~SELECT;
2748 EM_select_face(efa, 0);
2751 tface->flag &= ~(TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4);
2752 } else if (em->selectmode == SCE_SELECT_FACE) {
2753 if(tface->flag & (TF_SEL1|TF_SEL2|TF_SEL3))
2754 EM_select_face(efa, 0);
2755 else if(efa->v4 && tface->flag & TF_SEL4)
2756 EM_select_face(efa, 0);
2757 tface->flag &= ~(TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4);
2759 /* EM_deselect_flush will deselect the face */
2760 if(tface->flag & TF_SEL1) efa->v1->f &= ~SELECT;
2761 if(tface->flag & TF_SEL2) efa->v2->f &= ~SELECT;
2762 if(tface->flag & TF_SEL3) efa->v3->f &= ~SELECT;
2763 if((efa->v4) && tface->flag & TF_SEL4) efa->v4->f &= ~SELECT;
2764 tface->flag &= ~(TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4);
2771 /*deselects too many but ok for now*/
2772 if(em->selectmode & (SCE_SELECT_EDGE|SCE_SELECT_VERTEX)) {
2773 EM_deselect_flush(em);
2776 if (em->selectmode==SCE_SELECT_FACE) {
2777 /* de-selected all edges from faces that were de-selected.
2778 * now make sure all faces that are selected also have selected edges */
2779 for (efa= em->faces.first; efa; efa= efa->next) {
2780 if (efa->f & SELECT) {
2781 EM_select_face(efa, 1);
2786 EM_validate_selections();
2788 // XXX object_tface_flags_changed(OBACT, 0);
2792 void reveal_tface_uv(EditMesh *em)
2795 /* function should move away? */
2799 if( is_uv_tface_editing_allowed()==0 ) return;
2801 /* call the mesh function if we are in mesh sync sel */
2802 if (G.sima->flag & SI_SYNC_UVSEL) {
2807 if (G.sima->flag & SI_SELACTFACE) {
2808 if (em->selectmode == SCE_SELECT_FACE) {
2809 for (efa= em->faces.first; efa; efa= efa->next) {
2810 if (!(efa->h) && !(efa->f & SELECT)) {
2811 tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
2812 EM_select_face(efa, 1);
2813 tface->flag |= TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4;
2817 /* enable adjacent faces to have disconnected UV selections if sticky is disabled */
2818 if (G.sima->sticky == SI_STICKY_DISABLE) {
2819 for (efa= em->faces.first; efa; efa= efa->next) {
2820 if (!(efa->h) && !(efa->f & SELECT)) {
2821 /* All verts must be unselected for the face to be selected in the UV view */
2822 if ((efa->v1->f&SELECT)==0 && (efa->v2->f&SELECT)==0 && (efa->v3->f&SELECT)==0 && (efa->v4==0 || (efa->v4->f&SELECT)==0)) {
2823 tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
2824 tface->flag |= TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4;
2825 /* Cant use EM_select_face here because it unselects the verts
2826 * and we cant tell if the face was totally unselected or not */
2827 /*EM_select_face(efa, 1);
2829 * See Loop with EM_select_face() below... */
2835 for (efa= em->faces.first; efa; efa= efa->next) {
2836 if (!(efa->h) && !(efa->f & SELECT)) {
2837 tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
2838 if ((efa->v1->f & SELECT)==0) {tface->flag |= TF_SEL1;}
2839 if ((efa->v2->f & SELECT)==0) {tface->flag |= TF_SEL2;}
2840 if ((efa->v3->f & SELECT)==0) {tface->flag |= TF_SEL3;}
2841 if ((efa->v4 && (efa->v4->f & SELECT)==0)) {tface->flag |= TF_SEL4;}
2847 /* Select all edges and verts now */
2848 for (efa= em->faces.first; efa; efa= efa->next) {
2849 /* we only selected the face flags, and didnt changes edges or verts, fix this now */
2850 if (!(efa->h) && (efa->f & SELECT)) {
2851 EM_select_face(efa, 1);
2854 EM_select_flush(em);
2856 } else if (em->selectmode == SCE_SELECT_FACE) {
2857 for (efa= em->faces.first; efa; efa= efa->next) {
2858 if (!(efa->h) && !(efa->f & SELECT)) {
2859 tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
2861 tface->flag |= TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4;
2865 /* Select all edges and verts now */
2866 for (efa= em->faces.first; efa; efa= efa->next) {
2867 /* we only selected the face flags, and didnt changes edges or verts, fix this now */
2868 if (!(efa->h) && (efa->f & SELECT)) {
2869 EM_select_face(efa, 1);
2874 for (efa= em->faces.first; efa; efa= efa->next) {
2875 if (!(efa->h) && !(efa->f & SELECT)) {
2876 tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
2877 if ((efa->v1->f & SELECT)==0) {tface->flag |= TF_SEL1;}
2878 if ((efa->v2->f & SELECT)==0) {tface->flag |= TF_SEL2;}
2879 if ((efa->v3->f & SELECT)==0) {tface->flag |= TF_SEL3;}
2880 if ((efa->v4 && (efa->v4->f & SELECT)==0)) {tface->flag |= TF_SEL4;}
2885 /* Select all edges and verts now */
2886 for (efa= em->faces.first; efa; efa= efa->next) {
2887 /* we only selected the face flags, and didnt changes edges or verts, fix this now */
2888 if (!(efa->h) && (efa->f & SELECT)) {
2889 EM_select_face(efa, 1);
2894 // XXX object_tface_flags_changed(OBACT, 0);
2898 void select_faces_by_numverts(EditMesh *em, int numverts)
2902 /* Selects trias/qiads or isolated verts, and edges that do not have 2 neighboring
2906 /* for loose vertices/edges, we first select all, loop below will deselect */
2908 EM_set_flag_all(em, SELECT);
2909 else if(em->selectmode!=SCE_SELECT_FACE) {
2910 error("Only works in face selection mode");
2914 for(efa= em->faces.first; efa; efa= efa->next) {
2916 EM_select_face(efa, (numverts==4) );