Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / mesh / editmesh_select.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2004 Blender Foundation.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/mesh/editmesh_select.c
29  *  \ingroup edmesh
30  */
31
32 #include "MEM_guardedalloc.h"
33
34 #include "BLI_bitmap.h"
35 #include "BLI_bitmap_draw_2d.h"
36 #include "BLI_listbase.h"
37 #include "BLI_linklist.h"
38 #include "BLI_linklist_stack.h"
39 #include "BLI_math.h"
40 #include "BLI_math_bits.h"
41 #include "BLI_rand.h"
42 #include "BLI_array.h"
43
44 #include "BKE_context.h"
45 #include "BKE_report.h"
46 #include "BKE_paint.h"
47 #include "BKE_editmesh.h"
48
49 #include "IMB_imbuf_types.h"
50 #include "IMB_imbuf.h"
51
52 #include "WM_api.h"
53 #include "WM_types.h"
54
55 #include "RNA_access.h"
56 #include "RNA_define.h"
57 #include "RNA_enum_types.h"
58
59 #include "ED_mesh.h"
60 #include "ED_screen.h"
61 #include "ED_view3d.h"
62
63 #include "DNA_mesh_types.h"
64 #include "DNA_meshdata_types.h"
65 #include "DNA_object_types.h"
66
67 #include "UI_resources.h"
68
69 #include "bmesh_tools.h"
70
71 #include "DEG_depsgraph.h"
72
73 #include "mesh_intern.h"  /* own include */
74
75 /* use bmesh operator flags for a few operators */
76 #define BMO_ELE_TAG 1
77
78 /* ****************************** MIRROR **************** */
79
80 void EDBM_select_mirrored(
81         BMEditMesh *em, const int axis, const bool extend,
82         int *r_totmirr, int *r_totfail)
83 {
84         Mesh *me = (Mesh *)em->ob->data;
85         BMesh *bm = em->bm;
86         BMIter iter;
87         int totmirr = 0;
88         int totfail = 0;
89         bool use_topology = (me && (me->editflag & ME_EDIT_MIRROR_TOPO));
90
91         *r_totmirr = *r_totfail = 0;
92
93         /* select -> tag */
94         if (bm->selectmode & SCE_SELECT_VERTEX) {
95                 BMVert *v;
96                 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
97                         BM_elem_flag_set(v, BM_ELEM_TAG, BM_elem_flag_test(v, BM_ELEM_SELECT));
98                 }
99         }
100         else if (em->selectmode & SCE_SELECT_EDGE) {
101                 BMEdge *e;
102                 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
103                         BM_elem_flag_set(e, BM_ELEM_TAG, BM_elem_flag_test(e, BM_ELEM_SELECT));
104                 }
105         }
106         else {
107                 BMFace *f;
108                 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
109                         BM_elem_flag_set(f, BM_ELEM_TAG, BM_elem_flag_test(f, BM_ELEM_SELECT));
110                 }
111         }
112
113         EDBM_verts_mirror_cache_begin(em, axis, true, true, use_topology);
114
115         if (!extend)
116                 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
117
118
119         if (bm->selectmode & SCE_SELECT_VERTEX) {
120                 BMVert *v;
121                 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
122                         if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN) && BM_elem_flag_test(v, BM_ELEM_TAG)) {
123                                 BMVert *v_mirr = EDBM_verts_mirror_get(em, v);
124                                 if (v_mirr && !BM_elem_flag_test(v_mirr, BM_ELEM_HIDDEN)) {
125                                         BM_vert_select_set(bm, v_mirr, true);
126                                         totmirr++;
127                                 }
128                                 else {
129                                         totfail++;
130                                 }
131                         }
132                 }
133         }
134         else if (em->selectmode & SCE_SELECT_EDGE) {
135                 BMEdge *e;
136                 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
137                         if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && BM_elem_flag_test(e, BM_ELEM_TAG)) {
138                                 BMEdge *e_mirr = EDBM_verts_mirror_get_edge(em, e);
139                                 if (e_mirr && !BM_elem_flag_test(e_mirr, BM_ELEM_HIDDEN)) {
140                                         BM_edge_select_set(bm, e_mirr, true);
141                                         totmirr++;
142                                 }
143                                 else {
144                                         totfail++;
145                                 }
146                         }
147                 }
148         }
149         else {
150                 BMFace *f;
151                 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
152                         if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN) && BM_elem_flag_test(f, BM_ELEM_TAG)) {
153                                 BMFace *f_mirr = EDBM_verts_mirror_get_face(em, f);
154                                 if (f_mirr && !BM_elem_flag_test(f_mirr, BM_ELEM_HIDDEN)) {
155                                         BM_face_select_set(bm, f_mirr, true);
156                                         totmirr++;
157                                 }
158                                 else {
159                                         totfail++;
160                                 }
161                         }
162                 }
163         }
164
165         EDBM_verts_mirror_cache_end(em);
166
167         *r_totmirr = totmirr;
168         *r_totfail = totfail;
169 }
170
171 void EDBM_automerge(Scene *scene, Object *obedit, bool update, const char hflag)
172 {
173         bool ok;
174         BMEditMesh *em = BKE_editmesh_from_object(obedit);
175
176         ok = BMO_op_callf(em->bm, BMO_FLAG_DEFAULTS,
177                           "automerge verts=%hv dist=%f",
178                           hflag, scene->toolsettings->doublimit);
179
180         if (LIKELY(ok) && update) {
181                 EDBM_update_generic(em, true, true);
182         }
183 }
184
185 /* ****************************** SELECTION ROUTINES **************** */
186
187 unsigned int bm_solidoffs = 0, bm_wireoffs = 0, bm_vertoffs = 0;    /* set in drawobject.c ... for colorindices */
188
189 /* facilities for border select and circle select */
190 static BLI_bitmap *selbuf = NULL;
191
192 static BLI_bitmap *edbm_backbuf_alloc(const int size)
193 {
194         return BLI_BITMAP_NEW(size, "selbuf");
195 }
196
197 /* reads rect, and builds selection array for quick lookup */
198 /* returns if all is OK */
199 bool EDBM_backbuf_border_init(
200         const struct EvaluationContext *eval_ctx, ViewContext *vc, short xmin,
201         short ymin, short xmax, short ymax)
202 {
203         struct ImBuf *buf;
204         unsigned int *dr;
205         int a;
206         
207         if (vc->obedit == NULL || !V3D_IS_ZBUF(vc->v3d)) {
208                 return false;
209         }
210         
211         buf = ED_view3d_backbuf_read(eval_ctx, vc, xmin, ymin, xmax, ymax);
212         if ((buf == NULL) || (bm_vertoffs == 0)) {
213                 return false;
214         }
215
216         dr = buf->rect;
217         
218         /* build selection lookup */
219         selbuf = edbm_backbuf_alloc(bm_vertoffs + 1);
220         
221         a = (xmax - xmin + 1) * (ymax - ymin + 1);
222         while (a--) {
223                 if (*dr > 0 && *dr <= bm_vertoffs) {
224                         BLI_BITMAP_ENABLE(selbuf, *dr);
225                 }
226                 dr++;
227         }
228         IMB_freeImBuf(buf);
229         return true;
230 }
231
232 bool EDBM_backbuf_check(unsigned int index)
233 {
234         /* odd logic, if selbuf is NULL we assume no zbuf-selection is enabled
235          * and just ignore the depth buffer, this is error prone since its possible
236          * code doesn't set the depth buffer by accident, but leave for now. - Campbell */
237         if (selbuf == NULL)
238                 return true;
239
240         if (index > 0 && index <= bm_vertoffs)
241                 return BLI_BITMAP_TEST_BOOL(selbuf, index);
242
243         return false;
244 }
245
246 void EDBM_backbuf_free(void)
247 {
248         if (selbuf) MEM_freeN(selbuf);
249         selbuf = NULL;
250 }
251
252 struct LassoMaskData {
253         unsigned int *px;
254         int width;
255 };
256
257 static void edbm_mask_lasso_px_cb(int x, int x_end, int y, void *user_data)
258 {
259         struct LassoMaskData *data = user_data;
260         unsigned int *px = &data->px[(y * data->width) + x];
261         do {
262                 *px = true;
263                 px++;
264         } while (++x != x_end);
265 }
266
267
268 /* mcords is a polygon mask
269  * - grab backbuffer,
270  * - draw with black in backbuffer, 
271  * - grab again and compare
272  * returns 'OK' 
273  */
274 bool EDBM_backbuf_border_mask_init(const struct EvaluationContext *eval_ctx, ViewContext *vc, const int mcords[][2], short tot, short xmin, short ymin, short xmax, short ymax)
275 {
276         unsigned int *dr, *dr_mask, *dr_mask_arr;
277         struct ImBuf *buf;
278         int a;
279         struct LassoMaskData lasso_mask_data;
280         
281         /* method in use for face selecting too */
282         if (vc->obedit == NULL) {
283                 if (!BKE_paint_select_elem_test(vc->obact)) {
284                         return false;
285                 }
286         }
287         else if (!V3D_IS_ZBUF(vc->v3d)) {
288                 return false;
289         }
290
291         buf = ED_view3d_backbuf_read(eval_ctx, vc, xmin, ymin, xmax, ymax);
292         if ((buf == NULL) || (bm_vertoffs == 0)) {
293                 return false;
294         }
295
296         dr = buf->rect;
297
298         dr_mask = dr_mask_arr = MEM_callocN(sizeof(*dr_mask) * buf->x * buf->y, __func__);
299         lasso_mask_data.px = dr_mask;
300         lasso_mask_data.width = (xmax - xmin) + 1;
301
302         BLI_bitmap_draw_2d_poly_v2i_n(
303                xmin, ymin, xmax + 1, ymax + 1,
304                mcords, tot,
305                edbm_mask_lasso_px_cb, &lasso_mask_data);
306
307         /* build selection lookup */
308         selbuf = edbm_backbuf_alloc(bm_vertoffs + 1);
309         
310         a = (xmax - xmin + 1) * (ymax - ymin + 1);
311         while (a--) {
312                 if (*dr > 0 && *dr <= bm_vertoffs && *dr_mask == true) {
313                         BLI_BITMAP_ENABLE(selbuf, *dr);
314                 }
315                 dr++; dr_mask++;
316         }
317         IMB_freeImBuf(buf);
318         MEM_freeN(dr_mask_arr);
319
320         return true;
321 }
322
323 /* circle shaped sample area */
324 bool EDBM_backbuf_circle_init(
325         const struct EvaluationContext *eval_ctx, ViewContext *vc,
326         short xs, short ys, short rads)
327 {
328         struct ImBuf *buf;
329         unsigned int *dr;
330         short xmin, ymin, xmax, ymax, xc, yc;
331         int radsq;
332         
333         /* method in use for face selecting too */
334         if (vc->obedit == NULL) {
335                 if (!BKE_paint_select_elem_test(vc->obact)) {
336                         return false;
337                 }
338         }
339         else if (!V3D_IS_ZBUF(vc->v3d)) {
340                 return false;
341         }
342
343         xmin = xs - rads; xmax = xs + rads;
344         ymin = ys - rads; ymax = ys + rads;
345         buf = ED_view3d_backbuf_read(eval_ctx, vc, xmin, ymin, xmax, ymax);
346         if ((buf == NULL) || (bm_vertoffs == 0)) {
347                 return false;
348         }
349
350         dr = buf->rect;
351         
352         /* build selection lookup */
353         selbuf = edbm_backbuf_alloc(bm_vertoffs + 1);
354         radsq = rads * rads;
355         for (yc = -rads; yc <= rads; yc++) {
356                 for (xc = -rads; xc <= rads; xc++, dr++) {
357                         if (xc * xc + yc * yc < radsq) {
358                                 if (*dr > 0 && *dr <= bm_vertoffs) {
359                                         BLI_BITMAP_ENABLE(selbuf, *dr);
360                                 }
361                         }
362                 }
363         }
364
365         IMB_freeImBuf(buf);
366         return true;
367         
368 }
369
370
371 /* -------------------------------------------------------------------- */
372
373 /** \name Find Nearest Vert/Edge/Face
374  *
375  * \note Screen-space manhatten distances are used here,
376  * since its faster and good enough for the purpose of selection.
377  *
378  * \note \a dist_bias is used so we can bias against selected items.
379  * when choosing between elements of a single type, but return the real distance
380  * to avoid the bias interfering with distance comparisons when mixing types.
381  * \{ */
382
383 #define FIND_NEAR_SELECT_BIAS 5
384 #define FIND_NEAR_CYCLE_THRESHOLD_MIN 3
385
386 struct NearestVertUserData_Hit {
387         float   dist;
388         float   dist_bias;
389         int     index;
390         BMVert *vert;
391 };
392
393 struct NearestVertUserData {
394         float mval_fl[2];
395         bool use_select_bias;
396         bool use_cycle;
397         int cycle_index_prev;
398
399         struct NearestVertUserData_Hit hit;
400         struct NearestVertUserData_Hit hit_cycle;
401 };
402
403 static void findnearestvert__doClosest(void *userData, BMVert *eve, const float screen_co[2], int index)
404 {
405         struct NearestVertUserData *data = userData;
406         float dist_test, dist_test_bias;
407
408         dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co);
409
410         if (data->use_select_bias && BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
411                 dist_test_bias += FIND_NEAR_SELECT_BIAS;
412         }
413
414         if (dist_test_bias < data->hit.dist_bias) {
415                 data->hit.dist_bias = dist_test_bias;
416                 data->hit.dist = dist_test;
417                 data->hit.index = index;
418                 data->hit.vert = eve;
419         }
420
421         if (data->use_cycle) {
422                 if ((data->hit_cycle.vert == NULL) &&
423                     (index > data->cycle_index_prev) &&
424                     (dist_test_bias < FIND_NEAR_CYCLE_THRESHOLD_MIN))
425                 {
426                         data->hit_cycle.dist_bias = dist_test_bias;
427                         data->hit_cycle.dist = dist_test;
428                         data->hit_cycle.index = index;
429                         data->hit_cycle.vert = eve;
430                 }
431         }
432 }
433
434 /**
435  * Nearest vertex under the cursor.
436  *
437  * \param r_dist (in/out), minimal distance to the nearest and at the end, actual distance
438  * \param use_select_bias
439  * - When true, selected vertice are given a 5 pixel bias to make them further than unselect verts.
440  * - When false, unselected vertice are given the bias.
441  * \param use_cycle Cycle over elements within #FIND_NEAR_CYCLE_THRESHOLD_MIN in order of index.
442  */
443 BMVert *EDBM_vert_find_nearest_ex(
444         const struct EvaluationContext *eval_ctx, ViewContext *vc, float *r_dist,
445         const bool use_select_bias, bool use_cycle)
446 {
447         BMesh *bm = vc->em->bm;
448
449         if (V3D_IS_ZBUF(vc->v3d)) {
450                 const int dist_px = ED_view3d_backbuf_sample_size_clamp(vc->ar, *r_dist);
451                 float dist_test;
452                 unsigned int index;
453                 BMVert *eve;
454                 
455                 /* No afterqueue (yet), so we check it now, otherwise the bm_xxxofs indices are bad. */
456                 ED_view3d_backbuf_validate(eval_ctx, vc);
457
458                 index = ED_view3d_backbuf_sample_rect(
459                         eval_ctx, vc, vc->mval, dist_px, bm_wireoffs, 0xFFFFFF, &dist_test);
460                 eve = index ? BM_vert_at_index_find_or_table(bm, index - 1) : NULL;
461                 
462                 if (eve) {
463                         if (dist_test < *r_dist) {
464                                 *r_dist = dist_test;
465                                 return eve;
466                         }
467                 }
468                 return NULL;
469         }
470         else {
471                 struct NearestVertUserData data = {{0}};
472                 const struct NearestVertUserData_Hit *hit;
473                 const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_DEFAULT;
474
475                 static int prev_select_index = 0;
476                 static const BMVert *prev_select_elem = NULL;
477
478                 if ((use_cycle == false) ||
479                     (prev_select_elem && (prev_select_elem != BM_vert_at_index_find_or_table(bm, prev_select_index))))
480                 {
481                         prev_select_index = 0;
482                         prev_select_elem = NULL;
483                 }
484
485                 data.mval_fl[0] = vc->mval[0];
486                 data.mval_fl[1] = vc->mval[1];
487                 data.use_select_bias = use_select_bias;
488                 data.use_cycle = use_cycle;
489                 data.hit.dist      = data.hit_cycle.dist = \
490                 data.hit.dist_bias = data.hit_cycle.dist_bias = *r_dist;
491                 data.cycle_index_prev = prev_select_index;
492
493                 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
494                 mesh_foreachScreenVert(eval_ctx, vc, findnearestvert__doClosest, &data, clip_flag);
495
496                 hit = (data.use_cycle && data.hit_cycle.vert) ? &data.hit_cycle : &data.hit;
497                 *r_dist = hit->dist;
498
499                 prev_select_elem = hit->vert;
500                 prev_select_index = hit->index;
501
502                 return hit->vert;
503         }
504 }
505
506 BMVert *EDBM_vert_find_nearest(const struct EvaluationContext *eval_ctx, ViewContext *vc, float *r_dist)
507 {
508         return EDBM_vert_find_nearest_ex(eval_ctx, vc, r_dist, false, false);
509 }
510
511 /* find the distance to the edge we already have */
512 struct NearestEdgeUserData_ZBuf {
513         float mval_fl[2];
514         float dist;
515         const BMEdge *edge_test;
516 };
517
518 static void find_nearest_edge_center__doZBuf(
519         void *userData, BMEdge *eed,
520         const float screen_co_a[2], const float screen_co_b[2],
521         int UNUSED(index))
522 {
523         struct NearestEdgeUserData_ZBuf *data = userData;
524
525         if (eed == data->edge_test) {
526                 float dist_test;
527                 float screen_co_mid[2];
528
529                 mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b);
530                 dist_test = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
531
532                 if (dist_test < data->dist) {
533                         data->dist = dist_test;
534                 }
535         }
536 }
537
538 struct NearestEdgeUserData_Hit {
539         float   dist;
540         float   dist_bias;
541         int     index;
542         BMEdge *edge;
543
544         /* edges only, un-biased manhatten distance to which ever edge we pick
545          * (not used for choosing) */
546         float   dist_center;
547 };
548
549 struct NearestEdgeUserData {
550         ViewContext vc;
551         float mval_fl[2];
552         bool use_select_bias;
553         bool use_cycle;
554         int cycle_index_prev;
555
556         struct NearestEdgeUserData_Hit hit;
557         struct NearestEdgeUserData_Hit hit_cycle;
558 };
559
560 /* note; uses v3d, so needs active 3d window */
561 static void find_nearest_edge__doClosest(
562         void *userData, BMEdge *eed,
563         const float screen_co_a[2], const float screen_co_b[2],
564         int index)
565 {
566         struct NearestEdgeUserData *data = userData;
567         float dist_test, dist_test_bias;
568
569         float fac = line_point_factor_v2(data->mval_fl, screen_co_a, screen_co_b);
570         float screen_co[2];
571
572         if (fac <= 0.0f) {
573                 fac = 0.0f;
574                 copy_v2_v2(screen_co, screen_co_a);
575         }
576         else if (fac >= 1.0f) {
577                 fac = 1.0f;
578                 copy_v2_v2(screen_co, screen_co_b);
579         }
580         else {
581                 interp_v2_v2v2(screen_co, screen_co_a, screen_co_b, fac);
582         }
583
584         dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co);
585
586         if (data->use_select_bias && BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
587                 dist_test_bias += FIND_NEAR_SELECT_BIAS;
588         }
589
590         if (data->vc.rv3d->rflag & RV3D_CLIPPING) {
591                 float vec[3];
592
593                 interp_v3_v3v3(vec, eed->v1->co, eed->v2->co, fac);
594                 if (ED_view3d_clipping_test(data->vc.rv3d, vec, true)) {
595                         return;
596                 }
597         }
598
599         if (dist_test_bias < data->hit.dist_bias) {
600                 float screen_co_mid[2];
601
602                 data->hit.dist_bias = dist_test_bias;
603                 data->hit.dist = dist_test;
604                 data->hit.index = index;
605                 data->hit.edge = eed;
606
607                 mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b);
608                 data->hit.dist_center = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
609         }
610
611         if (data->use_cycle) {
612                 if ((data->hit_cycle.edge == NULL) &&
613                     (index > data->cycle_index_prev) &&
614                     (dist_test_bias < FIND_NEAR_CYCLE_THRESHOLD_MIN))
615                 {
616                         float screen_co_mid[2];
617
618                         data->hit_cycle.dist_bias = dist_test_bias;
619                         data->hit_cycle.dist = dist_test;
620                         data->hit_cycle.index = index;
621                         data->hit_cycle.edge = eed;
622
623                         mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b);
624                         data->hit_cycle.dist_center = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
625                 }
626         }
627 }
628
629 BMEdge *EDBM_edge_find_nearest_ex(
630         const struct EvaluationContext *eval_ctx, ViewContext *vc, float *r_dist,
631         float *r_dist_center,
632         const bool use_select_bias, const bool use_cycle,
633         BMEdge **r_eed_zbuf)
634 {
635         BMesh *bm = vc->em->bm;
636
637         if (V3D_IS_ZBUF(vc->v3d)) {
638                 const int dist_px = ED_view3d_backbuf_sample_size_clamp(vc->ar, *r_dist);
639                 float dist_test = 0.0f;
640                 unsigned int index;
641                 BMEdge *eed;
642
643                 /* Make sure that the edges also are considered to find nearest.
644                  * TODO: cleanup: add `selectmode` as a parameter */
645                 const short ts_selectmode = vc->scene->toolsettings->selectmode;
646                 vc->scene->toolsettings->selectmode |= SCE_SELECT_EDGE;
647
648                 /* No afterqueue (yet), so we check it now, otherwise the bm_xxxofs indices are bad. */
649                 ED_view3d_backbuf_validate(eval_ctx, vc);
650
651                 /* restore `selectmode` */
652                 vc->scene->toolsettings->selectmode = ts_selectmode;
653
654                 index = ED_view3d_backbuf_sample_rect(eval_ctx, vc, vc->mval, dist_px, bm_solidoffs, bm_wireoffs, &dist_test);
655                 eed = index ? BM_edge_at_index_find_or_table(bm, index - 1) : NULL;
656
657                 if (r_eed_zbuf) {
658                         *r_eed_zbuf = eed;
659                 }
660
661                 /* exception for faces (verts don't need this) */
662                 if (r_dist_center && eed) {
663                         struct NearestEdgeUserData_ZBuf data;
664
665                         data.mval_fl[0] = vc->mval[0];
666                         data.mval_fl[1] = vc->mval[1];
667                         data.dist = FLT_MAX;
668                         data.edge_test = eed;
669
670                         ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
671
672                         mesh_foreachScreenEdge(eval_ctx, vc, find_nearest_edge_center__doZBuf, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
673
674                         *r_dist_center = data.dist;
675                 }
676                 /* end exception */
677
678                 if (eed) {
679                         if (dist_test < *r_dist) {
680                                 *r_dist = dist_test;
681                                 return eed;
682                         }
683                 }
684                 return NULL;
685         }
686         else {
687                 struct NearestEdgeUserData data = {{0}};
688                 const struct NearestEdgeUserData_Hit *hit;
689                 /* interpolate along the edge before doing a clipping plane test */
690                 const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_DEFAULT & ~V3D_PROJ_TEST_CLIP_BB;
691
692                 static int prev_select_index = 0;
693                 static const BMEdge *prev_select_elem = NULL;
694
695                 if ((use_cycle == false) ||
696                     (prev_select_elem && (prev_select_elem != BM_edge_at_index_find_or_table(bm, prev_select_index))))
697                 {
698                         prev_select_index = 0;
699                         prev_select_elem = NULL;
700                 }
701
702                 data.vc = *vc;
703                 data.mval_fl[0] = vc->mval[0];
704                 data.mval_fl[1] = vc->mval[1];
705                 data.use_select_bias = use_select_bias;
706                 data.use_cycle = use_cycle;
707                 data.hit.dist      = data.hit_cycle.dist = \
708                 data.hit.dist_bias = data.hit_cycle.dist_bias = *r_dist;
709                 data.cycle_index_prev = prev_select_index;
710
711                 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
712                 mesh_foreachScreenEdge(eval_ctx, vc, find_nearest_edge__doClosest, &data, clip_flag);
713
714                 hit = (data.use_cycle && data.hit_cycle.edge) ? &data.hit_cycle : &data.hit;
715                 *r_dist = hit->dist;
716                 if (r_dist_center) {
717                         *r_dist_center = hit->dist_center;
718                 }
719
720                 prev_select_elem = hit->edge;
721                 prev_select_index = hit->index;
722
723                 return hit->edge;
724         }
725 }
726
727 BMEdge *EDBM_edge_find_nearest(
728         const struct EvaluationContext *eval_ctx, ViewContext *vc, float *r_dist)
729 {
730         return EDBM_edge_find_nearest_ex(eval_ctx, vc, r_dist, NULL, false, false, NULL);
731 }
732
733 /* find the distance to the face we already have */
734 struct NearestFaceUserData_ZBuf {
735         float mval_fl[2];
736         float dist;
737         const BMFace *face_test;
738 };
739
740 static void find_nearest_face_center__doZBuf(void *userData, BMFace *efa, const float screen_co[2], int UNUSED(index))
741 {
742         struct NearestFaceUserData_ZBuf *data = userData;
743
744         if (efa == data->face_test) {
745                 const float dist_test = len_manhattan_v2v2(data->mval_fl, screen_co);
746
747                 if (dist_test < data->dist) {
748                         data->dist = dist_test;
749                 }
750         }
751 }
752
753
754 struct NearestFaceUserData_Hit {
755         float   dist;
756         float   dist_bias;
757         int     index;
758         BMFace *face;
759 };
760
761 struct NearestFaceUserData {
762         float mval_fl[2];
763         bool use_select_bias;
764         bool use_cycle;
765         int cycle_index_prev;
766
767         struct NearestFaceUserData_Hit hit;
768         struct NearestFaceUserData_Hit hit_cycle;
769 };
770
771 static void findnearestface__doClosest(void *userData, BMFace *efa, const float screen_co[2], int index)
772 {
773         struct NearestFaceUserData *data = userData;
774         float dist_test, dist_test_bias;
775
776         dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co);
777
778         if (data->use_select_bias && BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
779                 dist_test_bias += FIND_NEAR_SELECT_BIAS;
780         }
781
782         if (dist_test_bias < data->hit.dist_bias) {
783                 data->hit.dist_bias = dist_test_bias;
784                 data->hit.dist = dist_test;
785                 data->hit.index = index;
786                 data->hit.face = efa;
787         }
788
789         if (data->use_cycle) {
790                 if ((data->hit_cycle.face == NULL) &&
791                     (index > data->cycle_index_prev) &&
792                     (dist_test_bias < FIND_NEAR_CYCLE_THRESHOLD_MIN))
793                 {
794                         data->hit_cycle.dist_bias = dist_test_bias;
795                         data->hit_cycle.dist = dist_test;
796                         data->hit_cycle.index = index;
797                         data->hit_cycle.face = efa;
798                 }
799         }
800 }
801
802
803 BMFace *EDBM_face_find_nearest_ex(
804         const struct EvaluationContext *eval_ctx, ViewContext *vc, float *r_dist,
805         float *r_dist_center,
806         const bool use_select_bias, const bool use_cycle,
807         BMFace **r_efa_zbuf)
808 {
809         BMesh *bm = vc->em->bm;
810
811         if (V3D_IS_ZBUF(vc->v3d)) {
812                 float dist_test = 0.0f;
813                 unsigned int index;
814                 BMFace *efa;
815
816                 ED_view3d_backbuf_validate(eval_ctx, vc);
817
818                 index = ED_view3d_backbuf_sample(eval_ctx, vc, vc->mval[0], vc->mval[1]);
819                 efa = index ? BM_face_at_index_find_or_table(bm, index - 1) : NULL;
820                 
821                 if (r_efa_zbuf) {
822                         *r_efa_zbuf = efa;
823                 }
824
825                 /* exception for faces (verts don't need this) */
826                 if (r_dist_center && efa) {
827                         struct NearestFaceUserData_ZBuf data;
828
829                         data.mval_fl[0] = vc->mval[0];
830                         data.mval_fl[1] = vc->mval[1];
831                         data.dist = FLT_MAX;
832                         data.face_test = efa;
833
834                         ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
835
836                         mesh_foreachScreenFace(eval_ctx, vc, find_nearest_face_center__doZBuf, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
837
838                         *r_dist_center = data.dist;
839                 }
840                 /* end exception */
841
842                 if (efa) {
843                         if (dist_test < *r_dist) {
844                                 *r_dist = dist_test;
845                                 return efa;
846                         }
847                 }
848                 return NULL;
849         }
850         else {
851                 struct NearestFaceUserData data = {{0}};
852                 const struct NearestFaceUserData_Hit *hit;
853                 const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_DEFAULT;
854
855                 static int prev_select_index = 0;
856                 static const BMFace *prev_select_elem = NULL;
857
858                 if ((use_cycle == false) ||
859                     (prev_select_elem && (prev_select_elem != BM_face_at_index_find_or_table(bm, prev_select_index))))
860                 {
861                         prev_select_index = 0;
862                         prev_select_elem = NULL;
863                 }
864
865                 data.mval_fl[0] = vc->mval[0];
866                 data.mval_fl[1] = vc->mval[1];
867                 data.use_select_bias = use_select_bias;
868                 data.use_cycle = use_cycle;
869                 data.hit.dist      = data.hit_cycle.dist = \
870                 data.hit.dist_bias = data.hit_cycle.dist_bias = *r_dist;
871                 data.cycle_index_prev = prev_select_index;
872
873                 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
874                 mesh_foreachScreenFace(eval_ctx, vc, findnearestface__doClosest, &data, clip_flag);
875
876                 hit = (data.use_cycle && data.hit_cycle.face) ? &data.hit_cycle : &data.hit;
877                 *r_dist = hit->dist;
878                 if (r_dist_center) {
879                         *r_dist_center = hit->dist;
880                 }
881
882                 prev_select_elem = hit->face;
883                 prev_select_index = hit->index;
884
885                 return hit->face;
886         }
887 }
888
889 BMFace *EDBM_face_find_nearest(const struct EvaluationContext *eval_ctx, ViewContext *vc, float *r_dist)
890 {
891         return EDBM_face_find_nearest_ex(eval_ctx, vc, r_dist, NULL, false, false, NULL);
892 }
893
894 #undef FIND_NEAR_SELECT_BIAS
895 #undef FIND_NEAR_CYCLE_THRESHOLD_MIN
896
897
898 /* best distance based on screen coords. 
899  * use em->selectmode to define how to use 
900  * selected vertices and edges get disadvantage
901  * return 1 if found one
902  */
903 static int unified_findnearest(
904         const struct EvaluationContext *eval_ctx, ViewContext *vc,
905         BMVert **r_eve, BMEdge **r_eed, BMFace **r_efa)
906 {
907         BMEditMesh *em = vc->em;
908         static short mval_prev[2] = {-1, -1};
909         /* only cycle while the mouse remains still */
910         const bool use_cycle = ((mval_prev[0] == vc->mval[0]) && (mval_prev[1] == vc->mval[1]));
911         const float dist_init = ED_view3d_select_dist_px();
912         /* since edges select lines, we give dots advantage of ~20 pix */
913         const float dist_margin = (dist_init / 2);
914         float dist = dist_init;
915         BMFace *efa_zbuf = NULL;
916         BMEdge *eed_zbuf = NULL;
917         
918         BMVert *eve = NULL;
919         BMEdge *eed = NULL;
920         BMFace *efa = NULL;
921
922
923         /* no afterqueue (yet), so we check it now, otherwise the em_xxxofs indices are bad */
924         ED_view3d_backbuf_validate(eval_ctx, vc);
925
926         if ((dist > 0.0f) && em->selectmode & SCE_SELECT_FACE) {
927                 float dist_center = 0.0f;
928                 float *dist_center_p = (em->selectmode & (SCE_SELECT_EDGE | SCE_SELECT_VERTEX)) ? &dist_center : NULL;
929                 efa = EDBM_face_find_nearest_ex(eval_ctx, vc, &dist, dist_center_p, true, use_cycle, &efa_zbuf);
930                 if (efa && dist_center_p) {
931                         dist = min_ff(dist_margin, dist_center);
932                 }
933         }
934
935         if ((dist > 0.0f) && (em->selectmode & SCE_SELECT_EDGE)) {
936                 float dist_center = 0.0f;
937                 float *dist_center_p = (em->selectmode & SCE_SELECT_VERTEX) ? &dist_center : NULL;
938                 eed = EDBM_edge_find_nearest_ex(eval_ctx, vc, &dist, dist_center_p, true, use_cycle, &eed_zbuf);
939                 if (eed && dist_center_p) {
940                         dist = min_ff(dist_margin, dist_center);
941                 }
942         }
943
944         if ((dist > 0.0f) && em->selectmode & SCE_SELECT_VERTEX) {
945                 eve = EDBM_vert_find_nearest_ex(eval_ctx, vc, &dist, true, use_cycle);
946         }
947
948         /* return only one of 3 pointers, for frontbuffer redraws */
949         if (eve) {
950                 efa = NULL; eed = NULL;
951         }
952         else if (eed) {
953                 efa = NULL;
954         }
955
956         /* there may be a face under the cursor, who's center if too far away
957          * use this if all else fails, it makes sense to select this */
958         if ((eve || eed || efa) == 0) {
959                 if (eed_zbuf) {
960                         eed = eed_zbuf;
961                 }
962                 else if (efa_zbuf) {
963                         efa = efa_zbuf;
964                 }
965         }
966
967         mval_prev[0] = vc->mval[0];
968         mval_prev[1] = vc->mval[1];
969
970         *r_eve = eve;
971         *r_eed = eed;
972         *r_efa = efa;
973
974         return (eve || eed || efa);
975 }
976
977 /** \} */
978
979
980 /* ****************  SIMILAR "group" SELECTS. FACE, EDGE AND VERTEX ************** */
981 static const EnumPropertyItem prop_similar_compare_types[] = {
982         {SIM_CMP_EQ, "EQUAL", 0, "Equal", ""},
983         {SIM_CMP_GT, "GREATER", 0, "Greater", ""},
984         {SIM_CMP_LT, "LESS", 0, "Less", ""},
985
986         {0, NULL, 0, NULL, NULL}
987 };
988
989 static const EnumPropertyItem prop_similar_types[] = {
990         {SIMVERT_NORMAL, "NORMAL", 0, "Normal", ""},
991         {SIMVERT_FACE, "FACE", 0, "Amount of Adjacent Faces", ""},
992         {SIMVERT_VGROUP, "VGROUP", 0, "Vertex Groups", ""},
993         {SIMVERT_EDGE, "EDGE", 0, "Amount of connecting edges", ""},
994
995         {SIMEDGE_LENGTH, "LENGTH", 0, "Length", ""},
996         {SIMEDGE_DIR, "DIR", 0, "Direction", ""},
997         {SIMEDGE_FACE, "FACE", 0, "Amount of Faces Around an Edge", ""},
998         {SIMEDGE_FACE_ANGLE, "FACE_ANGLE", 0, "Face Angles", ""},
999         {SIMEDGE_CREASE, "CREASE", 0, "Crease", ""},
1000         {SIMEDGE_BEVEL, "BEVEL", 0, "Bevel", ""},
1001         {SIMEDGE_SEAM, "SEAM", 0, "Seam", ""},
1002         {SIMEDGE_SHARP, "SHARP", 0, "Sharpness", ""},
1003 #ifdef WITH_FREESTYLE
1004         {SIMEDGE_FREESTYLE, "FREESTYLE_EDGE", 0, "Freestyle Edge Marks", ""},
1005 #endif
1006
1007         {SIMFACE_MATERIAL, "MATERIAL", 0, "Material", ""},
1008         {SIMFACE_AREA, "AREA", 0, "Area", ""},
1009         {SIMFACE_SIDES, "SIDES", 0, "Polygon Sides", ""},
1010         {SIMFACE_PERIMETER, "PERIMETER", 0, "Perimeter", ""},
1011         {SIMFACE_NORMAL, "NORMAL", 0, "Normal", ""},
1012         {SIMFACE_COPLANAR, "COPLANAR", 0, "Co-planar", ""},
1013         {SIMFACE_SMOOTH, "SMOOTH", 0, "Flat/Smooth", ""},
1014         {SIMFACE_FACEMAP, "FACE_MAP", 0, "Face-Map", ""},
1015 #ifdef WITH_FREESTYLE
1016         {SIMFACE_FREESTYLE, "FREESTYLE_FACE", 0, "Freestyle Face Marks", ""},
1017 #endif
1018
1019         {0, NULL, 0, NULL, NULL}
1020 };
1021
1022 /* selects new faces/edges/verts based on the existing selection */
1023
1024 static int similar_face_select_exec(bContext *C, wmOperator *op)
1025 {
1026         Object *ob = CTX_data_edit_object(C);
1027         BMEditMesh *em = BKE_editmesh_from_object(ob);
1028         BMOperator bmop;
1029
1030         /* get the type from RNA */
1031         const int type = RNA_enum_get(op->ptr, "type");
1032         const float thresh = RNA_float_get(op->ptr, "threshold");
1033         const int compare = RNA_enum_get(op->ptr, "compare");
1034
1035         /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */
1036         EDBM_op_init(em, &bmop, op,
1037                      "similar_faces faces=%hf type=%i thresh=%f compare=%i",
1038                      BM_ELEM_SELECT, type, thresh, compare);
1039
1040         /* execute the operator */
1041         BMO_op_exec(em->bm, &bmop);
1042
1043         /* clear the existing selection */
1044         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
1045
1046         /* select the output */
1047         BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
1048
1049         /* finish the operator */
1050         if (!EDBM_op_finish(em, &bmop, op, true)) {
1051                 return OPERATOR_CANCELLED;
1052         }
1053
1054         EDBM_update_generic(em, false, false);
1055
1056         return OPERATOR_FINISHED;
1057 }       
1058
1059 /* ***************************************************** */
1060
1061 /* EDGE GROUP */
1062
1063 /* wrap the above function but do selection flushing edge to face */
1064 static int similar_edge_select_exec(bContext *C, wmOperator *op)
1065 {
1066         Object *ob = CTX_data_edit_object(C);
1067         BMEditMesh *em = BKE_editmesh_from_object(ob);
1068         BMOperator bmop;
1069
1070         /* get the type from RNA */
1071         const int type = RNA_enum_get(op->ptr, "type");
1072         const float thresh = RNA_float_get(op->ptr, "threshold");
1073         const int compare = RNA_enum_get(op->ptr, "compare");
1074
1075         /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */
1076         EDBM_op_init(em, &bmop, op,
1077                      "similar_edges edges=%he type=%i thresh=%f compare=%i",
1078                      BM_ELEM_SELECT, type, thresh, compare);
1079
1080         /* execute the operator */
1081         BMO_op_exec(em->bm, &bmop);
1082
1083         /* clear the existing selection */
1084         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
1085
1086         /* select the output */
1087         BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true);
1088         EDBM_selectmode_flush(em);
1089
1090         /* finish the operator */
1091         if (!EDBM_op_finish(em, &bmop, op, true)) {
1092                 return OPERATOR_CANCELLED;
1093         }
1094
1095         EDBM_update_generic(em, false, false);
1096
1097         return OPERATOR_FINISHED;
1098 }
1099
1100 /* ********************************* */
1101
1102 /*
1103  * VERT GROUP
1104  * mode 1: same normal
1105  * mode 2: same number of face users
1106  * mode 3: same vertex groups
1107  */
1108 static int similar_vert_select_exec(bContext *C, wmOperator *op)
1109 {
1110         Object *ob = CTX_data_edit_object(C);
1111         BMEditMesh *em = BKE_editmesh_from_object(ob);
1112         BMOperator bmop;
1113         /* get the type from RNA */
1114         const int type = RNA_enum_get(op->ptr, "type");
1115         const float thresh = RNA_float_get(op->ptr, "threshold");
1116         const int compare = RNA_enum_get(op->ptr, "compare");
1117
1118         /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */
1119         EDBM_op_init(em, &bmop, op,
1120                      "similar_verts verts=%hv type=%i thresh=%f compare=%i",
1121                      BM_ELEM_SELECT, type, thresh, compare);
1122
1123         /* execute the operator */
1124         BMO_op_exec(em->bm, &bmop);
1125
1126         /* clear the existing selection */
1127         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
1128
1129         /* select the output */
1130         BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "verts.out", BM_VERT, BM_ELEM_SELECT, true);
1131
1132         /* finish the operator */
1133         if (!EDBM_op_finish(em, &bmop, op, true)) {
1134                 return OPERATOR_CANCELLED;
1135         }
1136
1137         EDBM_selectmode_flush(em);
1138
1139         EDBM_update_generic(em, false, false);
1140
1141         return OPERATOR_FINISHED;
1142 }
1143
1144 static int edbm_select_similar_exec(bContext *C, wmOperator *op)
1145 {
1146         ToolSettings *ts = CTX_data_tool_settings(C);
1147         PropertyRNA *prop = RNA_struct_find_property(op->ptr, "threshold");
1148
1149         const int type = RNA_enum_get(op->ptr, "type");
1150
1151         if (!RNA_property_is_set(op->ptr, prop)) {
1152                 RNA_property_float_set(op->ptr, prop, ts->select_thresh);
1153         }
1154         else {
1155                 ts->select_thresh = RNA_property_float_get(op->ptr, prop);
1156         }
1157
1158         if      (type < 100) return similar_vert_select_exec(C, op);
1159         else if (type < 200) return similar_edge_select_exec(C, op);
1160         else                 return similar_face_select_exec(C, op);
1161 }
1162
1163 static const EnumPropertyItem *select_similar_type_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop),
1164                                                    bool *r_free)
1165 {
1166         Object *obedit;
1167
1168         if (!C) /* needed for docs and i18n tools */
1169                 return prop_similar_types;
1170
1171         obedit = CTX_data_edit_object(C);
1172
1173         if (obedit && obedit->type == OB_MESH) {
1174                 EnumPropertyItem *item = NULL;
1175                 int a, totitem = 0;
1176                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
1177
1178                 if (em->selectmode & SCE_SELECT_VERTEX) {
1179                         for (a = SIMVERT_NORMAL; a < SIMEDGE_LENGTH; a++) {
1180                                 RNA_enum_items_add_value(&item, &totitem, prop_similar_types, a);
1181                         }
1182                 }
1183                 else if (em->selectmode & SCE_SELECT_EDGE) {
1184                         for (a = SIMEDGE_LENGTH; a < SIMFACE_MATERIAL; a++) {
1185                                 RNA_enum_items_add_value(&item, &totitem, prop_similar_types, a);
1186                         }
1187                 }
1188                 else if (em->selectmode & SCE_SELECT_FACE) {
1189 #ifdef WITH_FREESTYLE
1190                         const int a_end = SIMFACE_FREESTYLE;
1191 #else
1192                         const int a_end = SIMFACE_FACEMAP;
1193 #endif
1194                         for (a = SIMFACE_MATERIAL; a <= a_end; a++) {
1195                                 RNA_enum_items_add_value(&item, &totitem, prop_similar_types, a);
1196                         }
1197                 }
1198                 RNA_enum_item_end(&item, &totitem);
1199
1200                 *r_free = true;
1201
1202                 return item;
1203         }
1204
1205         return prop_similar_types;
1206 }
1207
1208 void MESH_OT_select_similar(wmOperatorType *ot)
1209 {
1210         PropertyRNA *prop;
1211
1212         /* identifiers */
1213         ot->name = "Select Similar";
1214         ot->idname = "MESH_OT_select_similar";
1215         ot->description = "Select similar vertices, edges or faces by property types";
1216         
1217         /* api callbacks */
1218         ot->invoke = WM_menu_invoke;
1219         ot->exec = edbm_select_similar_exec;
1220         ot->poll = ED_operator_editmesh;
1221         
1222         /* flags */
1223         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1224         
1225         /* properties */
1226         prop = ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, SIMVERT_NORMAL, "Type", "");
1227         RNA_def_enum_funcs(prop, select_similar_type_itemf);
1228
1229         RNA_def_enum(ot->srna, "compare", prop_similar_compare_types, SIM_CMP_EQ, "Compare", "");
1230
1231         RNA_def_float(ot->srna, "threshold", 0.0f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f);
1232 }
1233
1234
1235 /* -------------------------------------------------------------------- */
1236 /* Select Similar Regions */
1237
1238 static int edbm_select_similar_region_exec(bContext *C, wmOperator *op)
1239 {
1240         Object *obedit = CTX_data_edit_object(C);
1241         BMEditMesh *em = BKE_editmesh_from_object(obedit);
1242         BMesh *bm = em->bm;
1243         bool changed = false;
1244
1245         /* group vars */
1246         int *groups_array;
1247         int (*group_index)[2];
1248         int group_tot;
1249         int i;
1250
1251         if (bm->totfacesel < 2) {
1252                 BKE_report(op->reports, RPT_ERROR, "No face regions selected");
1253                 return OPERATOR_CANCELLED;
1254         }
1255
1256         groups_array = MEM_mallocN(sizeof(*groups_array) * bm->totfacesel, __func__);
1257         group_tot = BM_mesh_calc_face_groups(bm, groups_array, &group_index,
1258                                              NULL, NULL,
1259                                              BM_ELEM_SELECT, BM_VERT);
1260
1261         BM_mesh_elem_table_ensure(bm, BM_FACE);
1262
1263         for (i = 0; i < group_tot; i++) {
1264                 ListBase faces_regions;
1265                 int tot;
1266
1267                 const int fg_sta = group_index[i][0];
1268                 const int fg_len = group_index[i][1];
1269                 int j;
1270                 BMFace **fg = MEM_mallocN(sizeof(*fg) * fg_len, __func__);
1271
1272
1273                 for (j = 0; j < fg_len; j++) {
1274                         fg[j] = BM_face_at_index(bm, groups_array[fg_sta + j]);
1275                 }
1276
1277                 tot = BM_mesh_region_match(bm, fg, fg_len, &faces_regions);
1278
1279                 MEM_freeN(fg);
1280
1281                 if (tot) {
1282                         LinkData *link;
1283                         while ((link = BLI_pophead(&faces_regions))) {
1284                                 BMFace *f, **faces = link->data;
1285                                 while ((f = *(faces++))) {
1286                                         BM_face_select_set(bm, f, true);
1287                                 }
1288                                 MEM_freeN(link->data);
1289                                 MEM_freeN(link);
1290
1291                                 changed = true;
1292                         }
1293                 }
1294         }
1295
1296         MEM_freeN(groups_array);
1297         MEM_freeN(group_index);
1298
1299         if (changed) {
1300                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit);
1301         }
1302         else {
1303                 BKE_report(op->reports, RPT_WARNING, "No matching face regions found");
1304         }
1305
1306         return OPERATOR_FINISHED;
1307 }
1308
1309 void MESH_OT_select_similar_region(wmOperatorType *ot)
1310 {
1311         /* identifiers */
1312         ot->name = "Select Similar Regions";
1313         ot->idname = "MESH_OT_select_similar_region";
1314         ot->description = "Select similar face regions to the current selection";
1315
1316         /* api callbacks */
1317         ot->exec = edbm_select_similar_region_exec;
1318         ot->poll = ED_operator_editmesh;
1319
1320         /* flags */
1321         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1322 }
1323
1324
1325 /* ****************  Mode Select *************** */
1326
1327 static int edbm_select_mode_exec(bContext *C, wmOperator *op)
1328 {
1329         const int type        = RNA_enum_get(op->ptr,    "type");
1330         const int action      = RNA_enum_get(op->ptr,    "action");
1331         const bool use_extend = RNA_boolean_get(op->ptr, "use_extend");
1332         const bool use_expand = RNA_boolean_get(op->ptr, "use_expand");
1333
1334         if (EDBM_selectmode_toggle(C, type, action, use_extend, use_expand)) {
1335                 return OPERATOR_FINISHED;
1336         }
1337         else {
1338                 return OPERATOR_CANCELLED;
1339         }
1340 }
1341
1342 static int edbm_select_mode_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1343 {
1344         /* detecting these options based on shift/ctrl here is weak, but it's done
1345          * to make this work when clicking buttons or menus */
1346         if (!RNA_struct_property_is_set(op->ptr, "use_extend"))
1347                 RNA_boolean_set(op->ptr, "use_extend", event->shift);
1348         if (!RNA_struct_property_is_set(op->ptr, "use_expand"))
1349                 RNA_boolean_set(op->ptr, "use_expand", event->ctrl);
1350
1351         return edbm_select_mode_exec(C, op);
1352 }
1353
1354 void MESH_OT_select_mode(wmOperatorType *ot)
1355 {
1356         PropertyRNA *prop;
1357
1358         static const EnumPropertyItem elem_items[] = {
1359                 {SCE_SELECT_VERTEX, "VERT", ICON_VERTEXSEL, "Vertices", ""},
1360                 {SCE_SELECT_EDGE,   "EDGE", ICON_EDGESEL, "Edges", ""},
1361                 {SCE_SELECT_FACE,   "FACE", ICON_FACESEL, "Faces", ""},
1362                 {0, NULL, 0, NULL, NULL},
1363         };
1364
1365         static const EnumPropertyItem actions_items[] = {
1366                 {0, "DISABLE", 0, "Disable", "Disable selected markers"},
1367                 {1, "ENABLE", 0, "Enable", "Enable selected markers"},
1368                 {2, "TOGGLE", 0, "Toggle", "Toggle disabled flag for selected markers"},
1369                 {0, NULL, 0, NULL, NULL}
1370         };
1371
1372         /* identifiers */
1373         ot->name = "Select Mode";
1374         ot->idname = "MESH_OT_select_mode";
1375         ot->description = "Change selection mode";
1376
1377         /* api callbacks */
1378         ot->invoke = edbm_select_mode_invoke;
1379         ot->exec = edbm_select_mode_exec;
1380         ot->poll = ED_operator_editmesh;
1381
1382         /* flags */
1383         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1384
1385         /* properties */
1386         prop = RNA_def_boolean(ot->srna, "use_extend", false, "Extend", "");
1387         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1388         prop = RNA_def_boolean(ot->srna, "use_expand", false, "Expand", "");
1389         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1390         ot->prop = prop = RNA_def_enum(ot->srna, "type", elem_items, 0, "Type", "");
1391         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1392
1393         RNA_def_enum(ot->srna, "action", actions_items, 2, "Action", "Selection action to execute");
1394 }
1395
1396 /* ***************************************************** */
1397
1398 /* ****************  LOOP SELECTS *************** */
1399
1400 static void walker_select_count(BMEditMesh *em, int walkercode, void *start, const bool select, const bool select_mix,
1401                                 int *r_totsel, int *r_totunsel)
1402 {
1403         BMesh *bm = em->bm;
1404         BMElem *ele;
1405         BMWalker walker;
1406         int tot[2] = {0, 0};
1407
1408         BMW_init(&walker, bm, walkercode,
1409                  BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP,
1410                  BMW_FLAG_TEST_HIDDEN,
1411                  BMW_NIL_LAY);
1412
1413         for (ele = BMW_begin(&walker, start); ele; ele = BMW_step(&walker)) {
1414                 tot[(BM_elem_flag_test_bool(ele, BM_ELEM_SELECT) != select)] += 1;
1415
1416                 if (!select_mix && tot[0] && tot[1]) {
1417                         tot[0] = tot[1] = -1;
1418                         break;
1419                 }
1420         }
1421
1422         *r_totsel = tot[0];
1423         *r_totunsel = tot[1];
1424
1425         BMW_end(&walker);
1426 }
1427
1428 static void walker_select(BMEditMesh *em, int walkercode, void *start, const bool select)
1429 {
1430         BMesh *bm = em->bm;
1431         BMElem *ele;
1432         BMWalker walker;
1433
1434         BMW_init(&walker, bm, walkercode,
1435                  BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP,
1436                  BMW_FLAG_TEST_HIDDEN,
1437                  BMW_NIL_LAY);
1438
1439         for (ele = BMW_begin(&walker, start); ele; ele = BMW_step(&walker)) {
1440                 if (!select) {
1441                         BM_select_history_remove(bm, ele);
1442                 }
1443                 BM_elem_select_set(bm, ele, select);
1444         }
1445         BMW_end(&walker);
1446 }
1447
1448 static int edbm_loop_multiselect_exec(bContext *C, wmOperator *op)
1449 {
1450         Object *obedit = CTX_data_edit_object(C);
1451         BMEditMesh *em = BKE_editmesh_from_object(obedit);
1452         BMEdge *eed;
1453         BMEdge **edarray;
1454         int edindex;
1455         const bool is_ring = RNA_boolean_get(op->ptr, "ring");
1456         
1457         BMIter iter;
1458         int totedgesel = 0;
1459
1460         BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
1461                 if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
1462                         totedgesel++;
1463                 }
1464         }
1465         
1466         edarray = MEM_mallocN(sizeof(BMEdge *) * totedgesel, "edge array");
1467         edindex = 0;
1468         
1469         BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
1470                 if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
1471                         edarray[edindex] = eed;
1472                         edindex++;
1473                 }
1474         }
1475         
1476         if (is_ring) {
1477                 for (edindex = 0; edindex < totedgesel; edindex += 1) {
1478                         eed = edarray[edindex];
1479                         walker_select(em, BMW_EDGERING, eed, true);
1480                 }
1481                 EDBM_selectmode_flush(em);
1482         }
1483         else {
1484                 for (edindex = 0; edindex < totedgesel; edindex += 1) {
1485                         eed = edarray[edindex];
1486                         walker_select(em, BMW_EDGELOOP, eed, true);
1487                 }
1488                 EDBM_selectmode_flush(em);
1489         }
1490         MEM_freeN(edarray);
1491 //      if (EM_texFaceCheck())
1492         
1493         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit);
1494
1495         return OPERATOR_FINISHED;
1496 }
1497
1498 void MESH_OT_loop_multi_select(wmOperatorType *ot)
1499 {
1500         /* identifiers */
1501         ot->name = "Multi Select Loops";
1502         ot->idname = "MESH_OT_loop_multi_select";
1503         ot->description = "Select a loop of connected edges by connection type";
1504         
1505         /* api callbacks */
1506         ot->exec = edbm_loop_multiselect_exec;
1507         ot->poll = ED_operator_editmesh;
1508         
1509         /* flags */
1510         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1511         
1512         /* properties */
1513         RNA_def_boolean(ot->srna, "ring", 0, "Ring", "");
1514 }
1515
1516                 
1517 /* ***************** MAIN MOUSE SELECTION ************** */
1518
1519
1520 /* ***************** loop select (non modal) ************** */
1521
1522 static void mouse_mesh_loop_face(BMEditMesh *em, BMEdge *eed, bool select, bool select_clear)
1523 {
1524         if (select_clear) {
1525                 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
1526         }
1527
1528         walker_select(em, BMW_FACELOOP, eed, select);
1529 }
1530
1531 static void mouse_mesh_loop_edge_ring(BMEditMesh *em, BMEdge *eed, bool select, bool select_clear)
1532 {
1533         if (select_clear) {
1534                 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
1535         }
1536
1537         walker_select(em, BMW_EDGERING, eed, select);
1538 }
1539
1540 static void mouse_mesh_loop_edge(BMEditMesh *em, BMEdge *eed, bool select, bool select_clear, bool select_cycle)
1541 {
1542         bool edge_boundary = false;
1543
1544         /* cycle between BMW_EDGELOOP / BMW_EDGEBOUNDARY  */
1545         if (select_cycle && BM_edge_is_boundary(eed)) {
1546                 int tot[2];
1547
1548                 /* if the loops selected toggle the boundaries */
1549                 walker_select_count(em, BMW_EDGELOOP, eed, select, false,
1550                                     &tot[0], &tot[1]);
1551                 if (tot[select] == 0) {
1552                         edge_boundary = true;
1553
1554                         /* if the boundaries selected, toggle back to the loop */
1555                         walker_select_count(em, BMW_EDGEBOUNDARY, eed, select, false,
1556                                             &tot[0], &tot[1]);
1557                         if (tot[select] == 0) {
1558                                 edge_boundary = false;
1559                         }
1560                 }
1561         }
1562
1563         if (select_clear) {
1564                 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
1565         }
1566
1567         if (edge_boundary) {
1568                 walker_select(em, BMW_EDGEBOUNDARY, eed, select);
1569         }
1570         else {
1571                 walker_select(em, BMW_EDGELOOP, eed, select);
1572         }
1573 }
1574
1575
1576 static bool mouse_mesh_loop(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle, bool ring)
1577 {
1578         EvaluationContext eval_ctx;
1579         ViewContext vc;
1580         BMEditMesh *em;
1581         BMEdge *eed;
1582         bool select = true;
1583         bool select_clear = false;
1584         bool select_cycle = true;
1585         float dist = ED_view3d_select_dist_px() * 0.6666f;
1586         float mvalf[2];
1587
1588         CTX_data_eval_ctx(C, &eval_ctx);
1589         em_setup_viewcontext(C, &vc);
1590         mvalf[0] = (float)(vc.mval[0] = mval[0]);
1591         mvalf[1] = (float)(vc.mval[1] = mval[1]);
1592         em = vc.em;
1593
1594         /* Make sure that the edges are also considered for selection.
1595          * TODO: cleanup: add `selectmode` as a parameter */
1596         const short ts_selectmode = vc.scene->toolsettings->selectmode;
1597         vc.scene->toolsettings->selectmode |= SCE_SELECT_EDGE;
1598
1599         /* no afterqueue (yet), so we check it now, otherwise the bm_xxxofs indices are bad */
1600         ED_view3d_backbuf_validate(&eval_ctx, &vc);
1601
1602         /* restore `selectmode` */
1603         vc.scene->toolsettings->selectmode = ts_selectmode;
1604
1605         eed = EDBM_edge_find_nearest_ex(&eval_ctx, &vc, &dist, NULL, true, true, NULL);
1606         if (eed == NULL) {
1607                 return false;
1608         }
1609
1610         if (extend == false && deselect == false && toggle == false) {
1611                 select_clear = true;
1612         }
1613
1614         if (extend) {
1615                 select = true;
1616         }
1617         else if (deselect) {
1618                 select = false;
1619         }
1620         else if (select_clear || (BM_elem_flag_test(eed, BM_ELEM_SELECT) == 0)) {
1621                 select = true;
1622         }
1623         else if (toggle) {
1624                 select = false;
1625                 select_cycle = false;
1626         }
1627
1628         if (em->selectmode & SCE_SELECT_FACE) {
1629                 mouse_mesh_loop_face(em, eed, select, select_clear);
1630         }
1631         else {
1632                 if (ring) {
1633                         mouse_mesh_loop_edge_ring(em, eed, select, select_clear);
1634                 }
1635                 else {
1636                         mouse_mesh_loop_edge(em, eed, select, select_clear, select_cycle);
1637                 }
1638         }
1639
1640         EDBM_selectmode_flush(em);
1641
1642         /* sets as active, useful for other tools */
1643         if (select) {
1644                 if (em->selectmode & SCE_SELECT_VERTEX) {
1645                         /* Find nearest vert from mouse
1646                          * (initialize to large values incase only one vertex can be projected) */
1647                         float v1_co[2], v2_co[2];
1648                         float length_1 = FLT_MAX;
1649                         float length_2 = FLT_MAX;
1650
1651                         /* We can't be sure this has already been set... */
1652                         ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
1653
1654                         if (ED_view3d_project_float_object(vc.ar, eed->v1->co, v1_co, V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK) {
1655                                 length_1 = len_squared_v2v2(mvalf, v1_co);
1656                         }
1657
1658                         if (ED_view3d_project_float_object(vc.ar, eed->v2->co, v2_co, V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK) {
1659                                 length_2 = len_squared_v2v2(mvalf, v2_co);
1660                         }
1661 #if 0
1662                         printf("mouse to v1: %f\nmouse to v2: %f\n", len_squared_v2v2(mvalf, v1_co),
1663                                len_squared_v2v2(mvalf, v2_co));
1664 #endif
1665                         BM_select_history_store(em->bm, (length_1 < length_2) ? eed->v1 : eed->v2);
1666                 }
1667                 else if (em->selectmode & SCE_SELECT_EDGE) {
1668                         BM_select_history_store(em->bm, eed);
1669                 }
1670                 else if (em->selectmode & SCE_SELECT_FACE) {
1671                         /* Select the face of eed which is the nearest of mouse. */
1672                         BMFace *f, *efa = NULL;
1673                         BMIter iterf;
1674                         float best_dist = FLT_MAX;
1675
1676                         /* We can't be sure this has already been set... */
1677                         ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
1678
1679                         BM_ITER_ELEM (f, &iterf, eed, BM_FACES_OF_EDGE) {
1680                                 if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
1681                                         float cent[3];
1682                                         float co[2], tdist;
1683
1684                                         BM_face_calc_center_mean(f, cent);
1685                                         if (ED_view3d_project_float_object(vc.ar, cent, co, V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK) {
1686                                                 tdist = len_squared_v2v2(mvalf, co);
1687                                                 if (tdist < best_dist) {
1688 /*                                                      printf("Best face: %p (%f)\n", f, tdist);*/
1689                                                         best_dist = tdist;
1690                                                         efa = f;
1691                                                 }
1692                                         }
1693                                 }
1694                         }
1695                         if (efa) {
1696                                 BM_mesh_active_face_set(em->bm, efa);
1697                                 BM_select_history_store(em->bm, efa);
1698                         }
1699                 }
1700         }
1701
1702         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit);
1703
1704         return true;
1705 }
1706
1707 static int edbm_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1708 {
1709         
1710         view3d_operator_needs_opengl(C);
1711         
1712         if (mouse_mesh_loop(C, event->mval,
1713                             RNA_boolean_get(op->ptr, "extend"),
1714                             RNA_boolean_get(op->ptr, "deselect"),
1715                             RNA_boolean_get(op->ptr, "toggle"),
1716                             RNA_boolean_get(op->ptr, "ring")))
1717         {
1718                 return OPERATOR_FINISHED;
1719         }
1720         else {
1721                 return OPERATOR_CANCELLED;
1722         }
1723 }
1724
1725 void MESH_OT_loop_select(wmOperatorType *ot)
1726 {
1727         /* identifiers */
1728         ot->name = "Loop Select";
1729         ot->idname = "MESH_OT_loop_select";
1730         ot->description = "Select a loop of connected edges";
1731         
1732         /* api callbacks */
1733         ot->invoke = edbm_select_loop_invoke;
1734         ot->poll = ED_operator_editmesh_region_view3d;
1735         
1736         /* flags */
1737         ot->flag = OPTYPE_UNDO;
1738         
1739         /* properties */
1740         RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "Extend the selection");
1741         RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Remove from the selection");
1742         RNA_def_boolean(ot->srna, "toggle", 0, "Toggle Select", "Toggle the selection");
1743         RNA_def_boolean(ot->srna, "ring", 0, "Select Ring", "Select ring");
1744 }
1745
1746 void MESH_OT_edgering_select(wmOperatorType *ot)
1747 {
1748         /* description */
1749         ot->name = "Edge Ring Select";
1750         ot->idname = "MESH_OT_edgering_select";
1751         ot->description = "Select an edge ring";
1752         
1753         /* callbacks */
1754         ot->invoke = edbm_select_loop_invoke;
1755         ot->poll = ED_operator_editmesh_region_view3d;
1756         
1757         /* flags */
1758         ot->flag = OPTYPE_UNDO;
1759
1760         RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection");
1761         RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Remove from the selection");
1762         RNA_def_boolean(ot->srna, "toggle", 0, "Toggle Select", "Toggle the selection");
1763         RNA_def_boolean(ot->srna, "ring", 1, "Select Ring", "Select ring");
1764 }
1765
1766 /* ******************** (de)select all operator **************** */
1767 static int edbm_select_all_exec(bContext *C, wmOperator *op)
1768 {
1769         Object *obedit = CTX_data_edit_object(C);
1770         BMEditMesh *em = BKE_editmesh_from_object(obedit);
1771         const int action = RNA_enum_get(op->ptr, "action");
1772
1773         switch (action) {
1774                 case SEL_TOGGLE:
1775                         EDBM_select_toggle_all(em);
1776                         break;
1777                 case SEL_SELECT:
1778                         EDBM_flag_enable_all(em, BM_ELEM_SELECT);
1779                         break;
1780                 case SEL_DESELECT:
1781                         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
1782                         break;
1783                 case SEL_INVERT:
1784                         EDBM_select_swap(em);
1785                         EDBM_selectmode_flush(em);
1786                         break;
1787         }
1788
1789         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit);
1790
1791         return OPERATOR_FINISHED;
1792 }
1793
1794 void MESH_OT_select_all(wmOperatorType *ot)
1795 {
1796         /* identifiers */
1797         ot->name = "(De)select All";
1798         ot->idname = "MESH_OT_select_all";
1799         ot->description = "(De)select all vertices, edges or faces";
1800
1801         /* api callbacks */
1802         ot->exec = edbm_select_all_exec;
1803         ot->poll = ED_operator_editmesh;
1804
1805         /* flags */
1806         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1807
1808         WM_operator_properties_select_all(ot);
1809 }
1810
1811 static int edbm_faces_select_interior_exec(bContext *C, wmOperator *UNUSED(op))
1812 {
1813         Object *obedit = CTX_data_edit_object(C);
1814         BMEditMesh *em = BKE_editmesh_from_object(obedit);
1815
1816         if (EDBM_select_interior_faces(em)) {
1817                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit);
1818
1819                 return OPERATOR_FINISHED;
1820         }
1821         else {
1822                 return OPERATOR_CANCELLED;
1823         }
1824
1825 }
1826
1827 void MESH_OT_select_interior_faces(wmOperatorType *ot)
1828 {
1829         /* identifiers */
1830         ot->name = "Select Interior Faces";
1831         ot->idname = "MESH_OT_select_interior_faces";
1832         ot->description = "Select faces where all edges have more than 2 face users";
1833
1834         /* api callbacks */
1835         ot->exec = edbm_faces_select_interior_exec;
1836         ot->poll = ED_operator_editmesh;
1837
1838         /* flags */
1839         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1840 }
1841
1842
1843 /* ************************************************** */
1844 /* here actual select happens */
1845 /* gets called via generic mouse select operator */
1846 bool EDBM_select_pick(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
1847 {
1848         EvaluationContext eval_ctx;
1849         ViewContext vc;
1850         BMVert *eve = NULL;
1851         BMEdge *eed = NULL;
1852         BMFace *efa = NULL;
1853
1854         /* setup view context for argument to callbacks */
1855         CTX_data_eval_ctx(C, &eval_ctx);
1856         em_setup_viewcontext(C, &vc);
1857         vc.mval[0] = mval[0];
1858         vc.mval[1] = mval[1];
1859
1860         if (unified_findnearest(&eval_ctx, &vc, &eve, &eed, &efa)) {
1861
1862                 /* Deselect everything */
1863                 if (extend == false && deselect == false && toggle == false)
1864                         EDBM_flag_disable_all(vc.em, BM_ELEM_SELECT);
1865
1866                 if (efa) {
1867                         if (extend) {
1868                                 /* set the last selected face */
1869                                 BM_mesh_active_face_set(vc.em->bm, efa);
1870
1871                                 /* Work-around: deselect first, so we can guarantee it will */
1872                                 /* be active even if it was already selected */
1873                                 BM_select_history_remove(vc.em->bm, efa);
1874                                 BM_face_select_set(vc.em->bm, efa, false);
1875                                 BM_select_history_store(vc.em->bm, efa);
1876                                 BM_face_select_set(vc.em->bm, efa, true);
1877                         }
1878                         else if (deselect) {
1879                                 BM_select_history_remove(vc.em->bm, efa);
1880                                 BM_face_select_set(vc.em->bm, efa, false);
1881                         }
1882                         else {
1883                                 /* set the last selected face */
1884                                 BM_mesh_active_face_set(vc.em->bm, efa);
1885
1886                                 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1887                                         BM_select_history_store(vc.em->bm, efa);
1888                                         BM_face_select_set(vc.em->bm, efa, true);
1889                                 }
1890                                 else if (toggle) {
1891                                         BM_select_history_remove(vc.em->bm, efa);
1892                                         BM_face_select_set(vc.em->bm, efa, false);
1893                                 }
1894                         }
1895                 }
1896                 else if (eed) {
1897                         if (extend) {
1898                                 /* Work-around: deselect first, so we can guarantee it will */
1899                                 /* be active even if it was already selected */
1900                                 BM_select_history_remove(vc.em->bm, eed);
1901                                 BM_edge_select_set(vc.em->bm, eed, false);
1902                                 BM_select_history_store(vc.em->bm, eed);
1903                                 BM_edge_select_set(vc.em->bm, eed, true);
1904                         }
1905                         else if (deselect) {
1906                                 BM_select_history_remove(vc.em->bm, eed);
1907                                 BM_edge_select_set(vc.em->bm, eed, false);
1908                         }
1909                         else {
1910                                 if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
1911                                         BM_select_history_store(vc.em->bm, eed);
1912                                         BM_edge_select_set(vc.em->bm, eed, true);
1913                                 }
1914                                 else if (toggle) {
1915                                         BM_select_history_remove(vc.em->bm, eed);
1916                                         BM_edge_select_set(vc.em->bm, eed, false);
1917                                 }
1918                         }
1919                 }
1920                 else if (eve) {
1921                         if (extend) {
1922                                 /* Work-around: deselect first, so we can guarantee it will */
1923                                 /* be active even if it was already selected */
1924                                 BM_select_history_remove(vc.em->bm, eve);
1925                                 BM_vert_select_set(vc.em->bm, eve, false);
1926                                 BM_select_history_store(vc.em->bm, eve);
1927                                 BM_vert_select_set(vc.em->bm, eve, true);
1928                         }
1929                         else if (deselect) {
1930                                 BM_select_history_remove(vc.em->bm, eve);
1931                                 BM_vert_select_set(vc.em->bm, eve, false);
1932                         }
1933                         else {
1934                                 if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
1935                                         BM_select_history_store(vc.em->bm, eve);
1936                                         BM_vert_select_set(vc.em->bm, eve, true);
1937                                 }
1938                                 else if (toggle) {
1939                                         BM_select_history_remove(vc.em->bm, eve);
1940                                         BM_vert_select_set(vc.em->bm, eve, false);
1941                                 }
1942                         }
1943                 }
1944
1945                 EDBM_selectmode_flush(vc.em);
1946
1947                 if (efa) {
1948                         /* Change active material on object. */
1949                         if (efa->mat_nr != vc.obedit->actcol - 1) {
1950                                 vc.obedit->actcol = efa->mat_nr + 1;
1951                                 vc.em->mat_nr = efa->mat_nr;
1952                                 WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, NULL);
1953                         }
1954
1955                         /* Change active face-map on object. */
1956                         if (!BLI_listbase_is_empty(&vc.obedit->fmaps)) {
1957                                 const int cd_fmap_offset = CustomData_get_offset(&vc.em->bm->pdata, CD_FACEMAP);
1958                                 if (cd_fmap_offset != -1) {
1959                                         int map = *((int *)BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset));
1960                                         if ((map < -1) || (map > BLI_listbase_count_ex(&vc.obedit->fmaps, map))) {
1961                                                 map = -1;
1962                                         }
1963                                         map += 1;
1964                                         if (map != vc.obedit->actfmap) {
1965                                                 /* We may want to add notifiers later,
1966                                                  * currently select update handles redraw. */
1967                                                 vc.obedit->actfmap = map;
1968                                         }
1969                                 }
1970                         }
1971
1972                 }
1973
1974                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit);
1975                 return true;
1976         }
1977
1978         return false;
1979 }
1980
1981 static void edbm_strip_selections(BMEditMesh *em)
1982 {
1983         BMEditSelection *ese, *nextese;
1984
1985         if (!(em->selectmode & SCE_SELECT_VERTEX)) {
1986                 ese = em->bm->selected.first;
1987                 while (ese) {
1988                         nextese = ese->next;
1989                         if (ese->htype == BM_VERT) BLI_freelinkN(&(em->bm->selected), ese);
1990                         ese = nextese;
1991                 }
1992         }
1993         if (!(em->selectmode & SCE_SELECT_EDGE)) {
1994                 ese = em->bm->selected.first;
1995                 while (ese) {
1996                         nextese = ese->next;
1997                         if (ese->htype == BM_EDGE) BLI_freelinkN(&(em->bm->selected), ese);
1998                         ese = nextese;
1999                 }
2000         }
2001         if (!(em->selectmode & SCE_SELECT_FACE)) {
2002                 ese = em->bm->selected.first;
2003                 while (ese) {
2004                         nextese = ese->next;
2005                         if (ese->htype == BM_FACE) BLI_freelinkN(&(em->bm->selected), ese);
2006                         ese = nextese;
2007                 }
2008         }
2009 }
2010
2011 /* when switching select mode, makes sure selection is consistent for editing */
2012 /* also for paranoia checks to make sure edge or face mode works */
2013 void EDBM_selectmode_set(BMEditMesh *em)
2014 {
2015         BMVert *eve;
2016         BMEdge *eed;
2017         BMFace *efa;
2018         BMIter iter;
2019         
2020         em->bm->selectmode = em->selectmode;
2021
2022         edbm_strip_selections(em); /* strip BMEditSelections from em->selected that are not relevant to new mode */
2023         
2024         if (em->bm->totvertsel == 0 &&
2025             em->bm->totedgesel == 0 &&
2026             em->bm->totfacesel == 0)
2027         {
2028                 return;
2029         }
2030
2031         if (em->selectmode & SCE_SELECT_VERTEX) {
2032                 if (em->bm->totvertsel) {
2033                         EDBM_select_flush(em);
2034                 }
2035         }
2036         else if (em->selectmode & SCE_SELECT_EDGE) {
2037                 /* deselect vertices, and select again based on edge select */
2038                 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
2039                         BM_vert_select_set(em->bm, eve, false);
2040                 }
2041
2042                 if (em->bm->totedgesel) {
2043                         BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
2044                                 if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
2045                                         BM_edge_select_set(em->bm, eed, true);
2046                                 }
2047                         }
2048
2049                         /* selects faces based on edge status */
2050                         EDBM_selectmode_flush(em);
2051                 }
2052         }
2053         else if (em->selectmode & SCE_SELECT_FACE) {
2054                 /* deselect eges, and select again based on face select */
2055                 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
2056                         BM_edge_select_set(em->bm, eed, false);
2057                 }
2058
2059                 if (em->bm->totfacesel) {
2060                         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2061                                 if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
2062                                         BM_face_select_set(em->bm, efa, true);
2063                                 }
2064                         }
2065                 }
2066         }
2067 }
2068
2069 /**
2070  * Expand & Contract the Selection
2071  * (used when changing modes and Ctrl key held)
2072  *
2073  * Flush the selection up:
2074  * - vert -> edge
2075  * - vert -> face
2076  * - edge -> face
2077  *
2078  * Flush the selection down:
2079  * - face -> edge
2080  * - face -> vert
2081  * - edge -> vert
2082  */
2083 void EDBM_selectmode_convert(BMEditMesh *em, const short selectmode_old, const short selectmode_new)
2084 {
2085         BMesh *bm = em->bm;
2086
2087         BMVert *eve;
2088         BMEdge *eed;
2089         BMFace *efa;
2090         BMIter iter;
2091
2092         /* first tag-to-select, then select --- this avoids a feedback loop */
2093
2094         /* have to find out what the selectionmode was previously */
2095         if (selectmode_old == SCE_SELECT_VERTEX) {
2096                 if (bm->totvertsel == 0) {
2097                         /* pass */
2098                 }
2099                 else if (selectmode_new == SCE_SELECT_EDGE) {
2100                         /* flush up (vert -> edge) */
2101
2102                         /* select all edges associated with every selected vert */
2103                         BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
2104                                 BM_elem_flag_set(eed, BM_ELEM_TAG, BM_edge_is_any_vert_flag_test(eed, BM_ELEM_SELECT));
2105                         }
2106
2107                         BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
2108                                 if (BM_elem_flag_test(eed, BM_ELEM_TAG)) {
2109                                         BM_edge_select_set(bm, eed, true);
2110                                 }
2111                         }
2112                 }
2113                 else if (selectmode_new == SCE_SELECT_FACE) {
2114                         /* flush up (vert -> face) */
2115
2116                         /* select all faces associated with every selected vert */
2117                         BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2118                                 BM_elem_flag_set(efa, BM_ELEM_TAG, BM_face_is_any_vert_flag_test(efa, BM_ELEM_SELECT));
2119                         }
2120
2121                         BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2122                                 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
2123                                         BM_face_select_set(bm, efa, true);
2124                                 }
2125                         }
2126                 }
2127         }
2128         else if (selectmode_old == SCE_SELECT_EDGE) {
2129                 if (bm->totedgesel == 0) {
2130                         /* pass */
2131                 }
2132                 else if (selectmode_new == SCE_SELECT_FACE) {
2133                         /* flush up (edge -> face) */
2134
2135                         /* select all faces associated with every selected edge */
2136                         BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2137                                 BM_elem_flag_set(efa, BM_ELEM_TAG, BM_face_is_any_edge_flag_test(efa, BM_ELEM_SELECT));
2138                         }
2139
2140                         BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2141                                 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
2142                                         BM_face_select_set(bm, efa, true);
2143                                 }
2144                         }
2145                 }
2146                 else if (selectmode_new == SCE_SELECT_VERTEX) {
2147                         /* flush down (edge -> vert) */
2148
2149                         BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
2150                                 if (!BM_vert_is_all_edge_flag_test(eve, BM_ELEM_SELECT, true)) {
2151                                         BM_vert_select_set(bm, eve, false);
2152                                 }
2153                         }
2154                         /* deselect edges without both verts selected */
2155                         BM_mesh_deselect_flush(bm);
2156                 }
2157         }
2158         else if (selectmode_old == SCE_SELECT_FACE) {
2159                 if (bm->totfacesel == 0) {
2160                         /* pass */
2161                 }
2162                 else if (selectmode_new == SCE_SELECT_EDGE) {
2163                         /* flush down (face -> edge) */
2164
2165                         BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
2166                                 if (!BM_edge_is_all_face_flag_test(eed, BM_ELEM_SELECT, true)) {
2167                                         BM_edge_select_set(bm, eed, false);
2168                                 }
2169                         }
2170                 }
2171                 else if (selectmode_new == SCE_SELECT_VERTEX) {
2172                         /* flush down (face -> vert) */
2173
2174                         BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
2175                                 if (!BM_vert_is_all_face_flag_test(eve, BM_ELEM_SELECT, true)) {
2176                                         BM_vert_select_set(bm, eve, false);
2177                                 }
2178                         }
2179                         /* deselect faces without verts selected */
2180                         BM_mesh_deselect_flush(bm);
2181                 }
2182         }
2183 }
2184
2185 /* user facing function, does notification */
2186 bool EDBM_selectmode_toggle(bContext *C, const short selectmode_new,
2187                             const int action, const bool use_extend, const bool use_expand)
2188 {
2189         ToolSettings *ts = CTX_data_tool_settings(C);
2190         Object *obedit = CTX_data_edit_object(C);
2191         BMEditMesh *em = NULL;
2192         bool ret = false;
2193
2194         if (obedit && obedit->type == OB_MESH) {
2195                 em = BKE_editmesh_from_object(obedit);
2196         }
2197
2198         if (em == NULL) {
2199                 return ret;
2200         }
2201
2202         switch (action) {
2203                 case -1:
2204                         /* already set */
2205                         break;
2206                 case 0:  /* disable */
2207                         /* check we have something to do */
2208                         if ((em->selectmode & selectmode_new) == 0) {
2209                                 return false;
2210                         }
2211                         em->selectmode &= ~selectmode_new;
2212                         break;
2213                 case 1:  /* enable */
2214                         /* check we have something to do */
2215                         if ((em->selectmode & selectmode_new) != 0) {
2216                                 return false;
2217                         }
2218                         em->selectmode |= selectmode_new;
2219                         break;
2220                 case 2:  /* toggle */
2221                         /* can't disable this flag if its the only one set */
2222                         if (em->selectmode == selectmode_new) {
2223                                 return false;
2224                         }
2225                         em->selectmode ^= selectmode_new;
2226                         break;
2227                 default:
2228                         BLI_assert(0);
2229                         break;
2230         }
2231
2232         if (use_extend == 0 || em->selectmode == 0) {
2233                 if (use_expand) {
2234                         const short selmode_max = highest_order_bit_s(ts->selectmode);
2235                         EDBM_selectmode_convert(em, selmode_max, selectmode_new);
2236                 }
2237         }
2238
2239         switch (selectmode_new) {
2240                 case SCE_SELECT_VERTEX:
2241                         if (use_extend == 0 || em->selectmode == 0) {
2242                                 em->selectmode = SCE_SELECT_VERTEX;
2243                         }
2244                         ts->selectmode = em->selectmode;
2245                         EDBM_selectmode_set(em);
2246                         ret = true;
2247                         break;
2248                 case SCE_SELECT_EDGE:
2249                         if (use_extend == 0 || em->selectmode == 0) {
2250                                 em->selectmode = SCE_SELECT_EDGE;
2251                         }
2252                         ts->selectmode = em->selectmode;
2253                         EDBM_selectmode_set(em);
2254                         ret = true;
2255                         break;
2256                 case SCE_SELECT_FACE:
2257                         if (use_extend == 0 || em->selectmode == 0) {
2258                                 em->selectmode = SCE_SELECT_FACE;
2259                         }
2260                         ts->selectmode = em->selectmode;
2261                         EDBM_selectmode_set(em);
2262                         ret = true;
2263                         break;
2264                 default:
2265                         BLI_assert(0);
2266                         break;
2267         }
2268
2269         if (ret == true) {
2270                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2271                 WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL);
2272         }
2273
2274         return ret;
2275 }
2276
2277 /**
2278  * Use to disable a selectmode if its enabled, Using another mode as a fallback
2279  * if the disabled mode is the only mode set.
2280  *
2281  * \return true if the mode is changed.
2282  */
2283 bool EDBM_selectmode_disable(Scene *scene, BMEditMesh *em,
2284                              const short selectmode_disable,
2285                              const short selectmode_fallback)
2286 {
2287         /* note essential, but switch out of vertex mode since the
2288          * selected regions wont be nicely isolated after flushing */
2289         if (em->selectmode & selectmode_disable) {
2290                 if (em->selectmode == selectmode_disable) {
2291                         em->selectmode = selectmode_fallback;
2292                 }
2293                 else {
2294                         em->selectmode &= ~selectmode_disable;
2295                 }
2296                 scene->toolsettings->selectmode = em->selectmode;
2297                 EDBM_selectmode_set(em);
2298
2299                 WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, scene);
2300
2301                 return true;
2302         }
2303         else {
2304                 return false;
2305         }
2306 }
2307
2308 void EDBM_deselect_by_material(BMEditMesh *em, const short index, const bool select)
2309 {
2310         BMIter iter;
2311         BMFace *efa;
2312
2313         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2314                 if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN))
2315                         continue;
2316                 if (efa->mat_nr == index) {
2317                         BM_face_select_set(em->bm, efa, select);
2318                 }
2319         }
2320 }
2321
2322 void EDBM_select_toggle_all(BMEditMesh *em) /* exported for UV */
2323 {
2324         if (em->bm->totvertsel || em->bm->totedgesel || em->bm->totfacesel)
2325                 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
2326         else
2327                 EDBM_flag_enable_all(em, BM_ELEM_SELECT);
2328 }
2329
2330 void EDBM_select_swap(BMEditMesh *em) /* exported for UV */
2331 {
2332         BMIter iter;
2333         BMVert *eve;
2334         BMEdge *eed;
2335         BMFace *efa;
2336         
2337         if (em->bm->selectmode & SCE_SELECT_VERTEX) {
2338                 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
2339                         if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN))
2340                                 continue;
2341                         BM_vert_select_set(em->bm, eve, !BM_elem_flag_test(eve, BM_ELEM_SELECT));
2342                 }
2343         }
2344         else if (em->selectmode & SCE_SELECT_EDGE) {
2345                 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
2346                         if (BM_elem_flag_test(eed, BM_ELEM_HIDDEN))
2347                                 continue;
2348                         BM_edge_select_set(em->bm, eed, !BM_elem_flag_test(eed, BM_ELEM_SELECT));
2349                 }
2350         }
2351         else {
2352                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2353                         if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN))
2354                                 continue;
2355                         BM_face_select_set(em->bm, efa, !BM_elem_flag_test(efa, BM_ELEM_SELECT));
2356                 }
2357
2358         }
2359 //      if (EM_texFaceCheck())
2360 }
2361
2362 bool EDBM_select_interior_faces(BMEditMesh *em)
2363 {
2364         BMesh *bm = em->bm;
2365         BMIter iter;
2366         BMIter eiter;
2367         BMFace *efa;
2368         BMEdge *eed;
2369         bool ok;
2370         bool changed = false;
2371
2372         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2373                 if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN))
2374                         continue;
2375
2376
2377                 ok = true;
2378                 BM_ITER_ELEM (eed, &eiter, efa, BM_EDGES_OF_FACE) {
2379                         if (!BM_edge_face_count_is_over(eed, 2)) {
2380                                 ok = false;
2381                                 break;
2382                         }
2383                 }
2384
2385                 if (ok) {
2386                         BM_face_select_set(bm, efa, true);
2387                         changed = true;
2388                 }
2389         }
2390
2391         return changed;
2392 }
2393
2394
2395 /************************ Select Linked Operator *************************/
2396
2397 /* so we can have last-used default depend on selection mode (rare exception!) */
2398 #define USE_LINKED_SELECT_DEFAULT_HACK
2399
2400 struct DelimitData {
2401         int cd_loop_type;
2402         int cd_loop_offset;
2403 };
2404
2405 static bool select_linked_delimit_test(
2406         BMEdge *e, int delimit,
2407         const struct DelimitData *delimit_data)
2408 {
2409         BLI_assert(delimit);
2410
2411         if (delimit & BMO_DELIM_SEAM) {
2412                 if (BM_elem_flag_test(e, BM_ELEM_SEAM)) {
2413                         return true;
2414                 }
2415         }
2416
2417         if (delimit & BMO_DELIM_SHARP) {
2418                 if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) == 0) {
2419                         return true;
2420                 }
2421         }
2422
2423         if (delimit & BMO_DELIM_NORMAL) {
2424                 if (!BM_edge_is_contiguous(e)) {
2425                         return true;
2426                 }
2427         }
2428
2429         if (delimit & BMO_DELIM_MATERIAL) {
2430                 if (e->l && e->l->radial_next != e->l) {
2431                         const short mat_nr = e->l->f->mat_nr;
2432                         BMLoop *l_iter = e->l->radial_next;
2433                         do {
2434                                 if (l_iter->f->mat_nr != mat_nr) {
2435                                         return true;
2436                                 }
2437                         } while ((l_iter = l_iter->radial_next) != e->l);
2438                 }
2439         }
2440
2441         if (delimit & BMO_DELIM_UV) {
2442                 if (BM_edge_is_contiguous_loop_cd(e, delimit_data->cd_loop_type, delimit_data->cd_loop_offset) == 0) {
2443                         return true;
2444                 }
2445         }
2446
2447         return false;
2448 }
2449
2450 #ifdef USE_LINKED_SELECT_DEFAULT_HACK
2451 /**
2452  * Gets the default from the operator fallback to own last-used value
2453  * (selected based on mode)
2454  */
2455 static int select_linked_delimit_default_from_op(wmOperator *op, BMEditMesh *em)
2456 {
2457         static char delimit_last_store[2] = {0, BMO_DELIM_SEAM};
2458         int delimit_last_index = (em->selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) == 0;
2459         char *delimit_last = &delimit_last_store[delimit_last_index];
2460         PropertyRNA *prop_delimit = RNA_struct_find_property(op->ptr, "delimit");
2461         int delimit;
2462
2463         if (RNA_property_is_set(op->ptr, prop_delimit)) {
2464                 delimit = RNA_property_enum_get(op->ptr, prop_delimit);
2465                 *delimit_last = delimit;
2466         }
2467         else {
2468                 delimit = *delimit_last;
2469                 RNA_property_enum_set(op->ptr, prop_delimit, delimit);
2470         }
2471         return delimit;
2472 }
2473 #endif
2474
2475 static void select_linked_delimit_validate(BMesh *bm, int *delimit)
2476 {
2477         if ((*delimit) & BMO_DELIM_UV) {
2478                 if (!CustomData_has_layer(&bm->ldata, CD_MLOOPUV)) {
2479                         (*delimit) &= ~BMO_DELIM_UV;
2480                 }
2481         }
2482 }
2483
2484 static void select_linked_delimit_begin(BMesh *bm, int delimit)
2485 {
2486         struct DelimitData delimit_data = {0};
2487
2488         if (delimit & BMO_DELIM_UV) {
2489                 delimit_data.cd_loop_type = CD_MLOOPUV;
2490                 delimit_data.cd_loop_offset = CustomData_get_offset(&bm->ldata, delimit_data.cd_loop_type);
2491                 if (delimit_data.cd_loop_offset == -1) {
2492                         delimit &= ~BMO_DELIM_UV;
2493                 }
2494         }
2495
2496         /* grr, shouldn't need to alloc BMO flags here */
2497         BM_mesh_elem_toolflags_ensure(bm);
2498
2499         {
2500                 BMIter iter;
2501                 BMEdge *e;
2502
2503                 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
2504                         const bool is_walk_ok = (
2505                                 (select_linked_delimit_test(e, delimit, &delimit_data) == false));
2506
2507                         BMO_edge_flag_set(bm, e, BMO_ELE_TAG, is_walk_ok);
2508                 }
2509         }
2510 }
2511
2512 static void select_linked_delimit_end(BMEditMesh *em)
2513 {
2514         BMesh *bm = em->bm;
2515
2516         BM_mesh_elem_toolflags_clear(bm);
2517 }
2518
2519 static int edbm_select_linked_exec(bContext *C, wmOperator *op)
2520 {
2521         Object *obedit = CTX_data_edit_object(C);
2522         BMEditMesh *em = BKE_editmesh_from_object(obedit);
2523         BMesh *bm = em->bm;
2524         BMIter iter;
2525         BMWalker walker;
2526
2527 #ifdef USE_LINKED_SELECT_DEFAULT_HACK
2528         int delimit = select_linked_delimit_default_from_op(op, em);
2529 #else
2530         int delimit = RNA_enum_get(op->ptr, "delimit");
2531 #endif
2532
2533         select_linked_delimit_validate(bm, &delimit);
2534
2535         if (delimit) {
2536                 select_linked_delimit_begin(em->bm, delimit);
2537         }
2538
2539         if (em->selectmode & SCE_SELECT_VERTEX) {
2540                 BMVert *v;
2541
2542                 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
2543                         BM_elem_flag_set(v, BM_ELEM_TAG, BM_elem_flag_test(v, BM_ELEM_SELECT));
2544                 }
2545
2546                 /* exclude all delimited verts */
2547                 if (delimit) {
2548                         BMEdge *e;
2549                         BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
2550                                 if (!BMO_edge_flag_test(bm, e, BMO_ELE_TAG)) {
2551                                         BM_elem_flag_disable(e->v1, BM_ELEM_TAG);
2552                                         BM_elem_flag_disable(e->v2, BM_ELEM_TAG);
2553                                 }
2554                         }
2555                 }
2556
2557                 BMW_init(&walker, em->bm, delimit ? BMW_LOOP_SHELL_WIRE : BMW_VERT_SHELL,
2558                          BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP,
2559                          BMW_FLAG_TEST_HIDDEN,
2560                          BMW_NIL_LAY);
2561
2562                 if (delimit) {
2563                         BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
2564                                 if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
2565                                         BMElem *ele_walk;
2566                                         BMW_ITER (ele_walk, &walker, v) {
2567                                                 if (ele_walk->head.htype == BM_LOOP) {
2568                                                         BMVert *v_step = ((BMLoop *)ele_walk)->v;
2569                                                         BM_vert_select_set(em->bm, v_step, true);
2570                                                         BM_elem_flag_disable(v_step, BM_ELEM_TAG);
2571                                                 }
2572                                                 else {
2573                                                         BMEdge *e_step = (BMEdge *)ele_walk;
2574                                                         BLI_assert(ele_walk->head.htype == BM_EDGE);
2575                                                         BM_edge_select_set(em->bm, e_step, true);
2576                                                         BM_elem_flag_disable(e_step->v1, BM_ELEM_TAG);
2577                                                         BM_elem_flag_disable(e_step->v2, BM_ELEM_TAG);
2578                                                 }
2579                                         }
2580                                 }
2581                         }
2582                 }
2583                 else {
2584                         BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
2585                                 if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
2586                                         BMEdge *e_walk;
2587                                         BMW_ITER (e_walk, &walker, v) {
2588                                                 BM_edge_select_set(em->bm, e_walk, true);
2589                                                 BM_elem_flag_disable(e_walk, BM_ELEM_TAG);
2590                                         }
2591                                 }
2592                         }
2593                 }
2594
2595                 BMW_end(&walker);
2596
2597                 EDBM_selectmode_flush(em);
2598         }
2599         else if (em->selectmode & SCE_SELECT_EDGE) {
2600                 BMEdge *e;
2601
2602                 if (delimit) {
2603                         BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
2604                                 BM_elem_flag_set(
2605                                         e, BM_ELEM_TAG,
2606                                         (BM_elem_flag_test(e, BM_ELEM_SELECT) && BMO_edge_flag_test(bm, e, BMO_ELE_TAG)));
2607                         }
2608                 }
2609                 else {
2610                         BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
2611                                 BM_elem_flag_set(e, BM_ELEM_TAG, BM_elem_flag_test(e, BM_ELEM_SELECT));
2612                         }
2613                 }
2614
2615                 BMW_init(&walker, em->bm, delimit ? BMW_LOOP_SHELL_WIRE : BMW_VERT_SHELL,
2616                          BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP,
2617                          BMW_FLAG_TEST_HIDDEN,
2618                          BMW_NIL_LAY);
2619
2620                 if (delimit) {
2621                         BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
2622                                 if (BM_elem_flag_test(e, BM_ELEM_TAG)) {
2623                                         BMElem *ele_walk;
2624                                         BMW_ITER (ele_walk, &walker, e) {
2625                                                 if (ele_walk->head.htype == BM_LOOP) {
2626                                                         BMLoop *l_step = (BMLoop *)ele_walk;
2627                                                         BM_edge_select_set(em->bm, l_step->e, true);
2628                                                         BM_edge_select_set(em->bm, l_step->prev->e, true);
2629                                                         BM_elem_flag_disable(l_step->e, BM_ELEM_TAG);
2630                                                 }
2631                                                 else {
2632                                                         BMEdge *e_step = (BMEdge *)ele_walk;
2633                                                         BLI_assert(ele_walk->head.htype == BM_EDGE);
2634                                                         BM_edge_select_set(em->bm, e_step, true);
2635                                                         BM_elem_flag_disable(e_step, BM_ELEM_TAG);
2636                                                 }
2637                                         }
2638                                 }
2639                         }
2640                 }
2641                 else {
2642                         BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
2643                                 if (BM_elem_flag_test(e, BM_ELEM_TAG)) {
2644                                         BMEdge *e_walk;
2645                                         BMW_ITER (e_walk, &walker, e) {
2646                                                 BM_edge_select_set(em->bm, e_walk, true);
2647                                                 BM_elem_flag_disable(e_walk, BM_ELEM_TAG);
2648                                         }
2649                                 }
2650                         }
2651                 }
2652
2653                 BMW_end(&walker);
2654
2655                 EDBM_selectmode_flush(em);
2656         }
2657         else {
2658                 BMFace *f;
2659
2660                 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
2661                         BM_elem_flag_set(f, BM_ELEM_TAG, BM_elem_flag_test(f, BM_ELEM_SELECT));
2662                 }
2663
2664                 BMW_init(&walker, bm, BMW_ISLAND,
2665                          BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP,
2666                          BMW_FLAG_TEST_HIDDEN,
2667                          BMW_NIL_LAY);
2668
2669                 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
2670                         if (BM_elem_flag_test(f, BM_ELEM_TAG)) {
2671                                 BMFace *f_walk;
2672                                 BMW_ITER (f_walk, &walker, f) {
2673                                         BM_face_select_set(bm, f_walk, true);
2674                                         BM_elem_flag_disable(f_walk, BM_ELEM_TAG);
2675                                 }
2676                         }
2677                 }
2678
2679                 BMW_end(&walker);
2680         }
2681
2682         if (delimit) {
2683                 select_linked_delimit_end(em);
2684         }
2685
2686         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit);
2687
2688         return OPERATOR_FINISHED;
2689 }
2690
2691 void MESH_OT_select_linked(wmOperatorType *ot)
2692 {
2693         PropertyRNA *prop;
2694
2695         /* identifiers */
2696         ot->name = "Select Linked All";
2697         ot->idname = "MESH_OT_select_linked";
2698         ot->description = "Select all vertices connected to the current selection";
2699
2700         /* api callbacks */
2701         ot->exec = edbm_select_linked_exec;
2702         ot->poll = ED_operator_editmesh;
2703
2704         /* flags */
2705         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2706
2707         prop = RNA_def_enum_flag(ot->srna, "delimit", rna_enum_mesh_delimit_mode_items, BMO_DELIM_SEAM, "Delimit",
2708                                  "Delimit selected region");
2709 #ifdef USE_LINKED_SELECT_DEFAULT_HACK
2710         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2711 #else
2712         UNUSED_VARS(prop);
2713 #endif
2714 }
2715
2716 static int edbm_select_linked_pick_exec(bContext *C, wmOperator *op);
2717
2718 static void edbm_select_linked_pick_ex(BMEditMesh *em, BMElem *ele, bool sel, int delimit)
2719 {
2720         BMesh *bm = em->bm;
2721         BMWalker walker;
2722
2723         select_linked_delimit_validate(bm, &delimit);
2724
2725         if (delimit) {
2726                 select_linked_delimit_begin(bm, delimit);
2727         }
2728
2729         /* Note: logic closely matches 'edbm_select_linked_exec', keep in sync */
2730
2731         if (ele->head.htype == BM_VERT) {
2732                 BMVert *eve = (BMVert *)ele;
2733
2734                 BMW_init(&walker, bm, delimit ? BMW_LOOP_SHELL_WIRE : BMW_VERT_SHELL,
2735                          BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP,
2736                          BMW_FLAG_TEST_HIDDEN,
2737                          BMW_NIL_LAY);
2738
2739                 if (delimit) {
2740                         BMElem *ele_walk;
2741                         BMW_ITER (ele_walk, &walker, eve) {
2742                                 if (ele_walk->head.htype == BM_LOOP) {
2743                                         BMVert *v_step = ((BMLoop *)ele_walk)->v;
2744                                         BM_vert_select_set(bm, v_step, sel);
2745                                 }
2746                                 else {
2747                                         BMEdge *e_step = (BMEdge *)ele_walk;
2748                                         BLI_assert(ele_walk->head.htype == BM_EDGE);
2749                                         BM_edge_select_set(bm, e_step, sel);
2750                                 }
2751                         }
2752                 }
2753                 else {
2754                         BMEdge *e_walk;
2755                         BMW_ITER (e_walk, &walker, eve) {
2756                                 BM_edge_select_set(bm, e_walk, sel);
2757                         }
2758                 }
2759
2760                 BMW_end(&walker);
2761
2762                 EDBM_selectmode_flush(em);
2763         }
2764         else if (ele->head.htype == BM_EDGE) {
2765                 BMEdge *eed = (BMEdge *)ele;
2766
2767                 BMW_init(&walker, bm, delimit ? BMW_LOOP_SHELL_WIRE : BMW_VERT_SHELL,
2768                          BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP,
2769                          BMW_FLAG_TEST_HIDDEN,
2770                          BMW_NIL_LAY);
2771
2772                 if (delimit) {
2773                         BMElem *ele_walk;
2774                         BMW_ITER (ele_walk, &walker, eed) {
2775                                 if (ele_walk->head.htype == BM_LOOP) {
2776                                         BMEdge *e_step = ((BMLoop *)ele_walk)->e;
2777                                         BM_edge_select_set(bm, e_step, sel);
2778                                 }
2779                                 else {
2780                                         BMEdge *e_step = (BMEdge *)ele_walk;
2781                                         BLI_assert(ele_walk->head.htype == BM_EDGE);
2782                                         BM_edge_select_set(bm, e_step, sel);
2783                                 }
2784                         }
2785                 }
2786                 else {
2787                         BMEdge *e_walk;
2788                         BMW_ITER (e_walk, &walker, eed) {
2789                                 BM_edge_select_set(bm, e_walk, sel);
2790                         }
2791                 }
2792
2793                 BMW_end(&walker);
2794
2795                 EDBM_selectmode_flush(em);
2796         }
2797         else if (ele->head.htype == BM_FACE) {
2798                 BMFace *efa = (BMFace *)ele;
2799
2800                 BMW_init(&walker, bm, BMW_ISLAND,
2801                          BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP,
2802                          BMW_FLAG_TEST_HIDDEN,
2803                          BMW_NIL_LAY);
2804
2805                 {
2806                         BMFace *f_walk;
2807                         BMW_ITER (f_walk, &walker, efa) {
2808                                 BM_face_select_set(bm, f_walk, sel);
2809                                 BM_elem_flag_disable(f_walk, BM_ELEM_TAG);
2810                         }
2811                 }
2812
2813                 BMW_end(&walker);
2814         }
2815
2816         if (delimit) {
2817                 select_linked_delimit_end(em);
2818         }
2819 }
2820
2821 static int edbm_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2822 {
2823         Object *obedit = CTX_data_edit_object(C);
2824         EvaluationContext eval_ctx;
2825         ViewContext vc;
2826         BMEditMesh *em;
2827         BMesh *bm;
2828         BMVert *eve;
2829         BMEdge *eed;
2830         BMFace *efa;
2831         const bool sel = !RNA_boolean_get(op->ptr, "deselect");
2832         int index;
2833
2834         if (RNA_struct_property_is_set(op->ptr, "index")) {
2835                 return edbm_select_linked_pick_exec(C, op);
2836         }
2837
2838         /* unified_finednearest needs ogl */
2839         view3d_operator_needs_opengl(C);
2840
2841         /* setup view context for argument to callbacks */
2842         CTX_data_eval_ctx(C, &eval_ctx);
2843         em_setup_viewcontext(C, &vc);
2844         em = vc.em;
2845         bm = em->bm;
2846
2847         if (bm->totedge == 0) {
2848                 return OPERATOR_CANCELLED;
2849         }
2850
2851         vc.mval[0] = event->mval[0];
2852         vc.mval[1] = event->mval[1];
2853
2854         /* return warning! */
2855         if (unified_findnearest(&eval_ctx, &vc, &eve, &eed, &efa) == 0) {
2856                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit);
2857
2858                 return OPERATOR_CANCELLED;
2859         }
2860
2861 #ifdef USE_LINKED_SELECT_DEFAULT_HACK
2862         int delimit = select_linked_delimit_default_from_op(op, em);
2863 #else
2864         int delimit = RNA_enum_get(op->ptr, "delimit");
2865 #endif
2866
2867         BMElem *ele = EDBM_elem_from_selectmode(em, eve, eed, efa);
2868
2869         edbm_select_linked_pick_ex(em, ele, sel, delimit);
2870
2871         /* to support redo */
2872         BM_mesh_elem_index_ensure(bm, ele->head.htype);
2873         index = EDBM_elem_to_index_any(em, ele);
2874
2875         RNA_int_set(op->ptr, "index", index);
2876
2877         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit);
2878
2879         return OPERATOR_FINISHED;
2880 }
2881
2882
2883 static int edbm_select_linked_pick_exec(bContext *C, wmOperator *op)
2884 {
2885         Object *obedit = CTX_data_edit_object(C);
2886         BMEditMesh *em = BKE_editmesh_from_object(obedit);
2887         BMesh *bm = em->bm;
2888         int index;
2889         const bool sel = !RNA_boolean_get(op->ptr, "deselect");
2890
2891         index = RNA_int_get(op->ptr, "index");
2892         if (index < 0 || index >= (bm->totvert + bm->totedge + bm->totface)) {
2893                 return OPERATOR_CANCELLED;
2894         }
2895
2896         BMElem *ele = EDBM_elem_from_index_any(em, index);
2897
2898 #ifdef USE_LINKED_SELECT_DEFAULT_HACK
2899         int delimit = select_linked_delimit_default_from_op(op, em);
2900 #else
2901         int delimit = RNA_enum_get(op->ptr, "delimit");
2902 #endif
2903
2904         edbm_select_linked_pick_ex(em, ele, sel, delimit);
2905
2906         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit);
2907
2908         return OPERATOR_FINISHED;
2909 }
2910
2911 void MESH_OT_select_linked_pick(wmOperatorType *ot)
2912 {
2913         PropertyRNA *prop;
2914
2915         /* identifiers */
2916         ot->name = "Select Linked";
2917         ot->idname = "MESH_OT_select_linked_pick";
2918         ot->description = "(De)select all vertices linked to the edge under the mouse cursor";
2919         
2920         /* api callbacks */
2921         ot->invoke = edbm_select_linked_pick_invoke;
2922         ot->exec = edbm_select_linked_pick_exec;
2923         ot->poll = ED_operator_editmesh;
2924         
2925         /* flags */
2926         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2927         
2928         RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "");
2929         prop = RNA_def_enum_flag(ot->srna, "delimit", rna_enum_mesh_delimit_mode_items, BMO_DELIM_SEAM, "Delimit",
2930                                  "Delimit selected region");
2931 #ifdef USE_LINKED_SELECT_DEFAULT_HACK
2932         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2933 #endif
2934
2935         /* use for redo */
2936         prop = RNA_def_int(ot->srna, "index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
2937         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
2938 }
2939
2940
2941 static int edbm_select_face_by_sides_exec(bContext *C, wmOperator *op)
2942 {
2943         Object *obedit = CTX_data_edit_object(C);
2944         BMEditMesh *em = BKE_editmesh_from_object(obedit);
2945         BMFace *efa;
2946         BMIter iter;
2947         const int numverts = RNA_int_get(op->ptr, "number");
2948         const int type = RNA_enum_get(op->ptr, "type");
2949
2950         if (!RNA_boolean_get(op->ptr, "extend"))
2951                 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
2952
2953         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2954
2955                 bool select;
2956
2957                 switch (type) {
2958                         case 0:
2959                                 select = (efa->len < numverts);
2960                                 break;
2961                         case 1:
2962                                 select = (efa->len == numverts);
2963                                 break;
2964                         case 2:
2965                                 select = (efa->len > numverts);
2966                                 break;
2967                         case 3:
2968                                 select = (efa->len != numverts);
2969                                 break;
2970                         default:
2971                                 BLI_assert(0);
2972                                 select = false;
2973                                 break;
2974                 }
2975
2976                 if (select) {
2977                         BM_face_select_set(em->bm, efa, true);
2978                 }
2979         }
2980
2981         EDBM_selectmode_flush(em);
2982
2983         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2984         return OPERATOR_FINISHED;
2985 }
2986
2987 void MESH_OT_select_face_by_sides(wmOperatorType *ot)
2988 {
2989         static const EnumPropertyItem type_items[] = {
2990                 {0, "LESS", 0, "Less Than", ""},
2991                 {1, "EQUAL", 0, "Equal To", ""},
2992                 {2, "GREATER", 0, "Greater Than", ""},
2993                 {3, "NOTEQUAL", 0, "Not Equal To", ""},
2994                 {0, NULL, 0, NULL, NULL}
2995         };
2996
2997         /* identifiers */
2998         ot->name = "Select Faces by Sides";
2999         ot->description = "Select vertices or faces by the number of polygon sides";
3000         ot->idname = "MESH_OT_select_face_by_sides";
3001
3002         /* api callbacks */
3003         ot->exec = edbm_select_face_by_sides_exec;
3004         ot->poll = ED_operator_editmesh;
3005
3006         /* flags */
3007         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3008
3009         /* properties */
3010         RNA_def_int(ot->srna, "number", 4, 3, INT_MAX, "Number of Vertices", "", 3, INT_MAX);
3011         RNA_def_enum(ot->srna, "type", type_items, 1, "Type", "Type of comparison to make");
3012         RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend the selection");
3013 }
3014
3015
3016 static int edbm_select_loose_exec(bContext *C, wmOperator *op)
3017 {
3018         Object *obedit = CTX_data_edit_object(C);
3019         BMEditMesh *em = BKE_editmesh_from_object(obedit);
3020         BMesh *bm = em->bm;
3021         BMIter iter;
3022
3023         if (!RNA_boolean_get(op->ptr, "extend"))
3024                 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
3025
3026         if (em->selectmode & SCE_SELECT_VERTEX) {
3027                 BMVert *eve;
3028                 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
3029                         if (!eve->e) {
3030                                 BM_vert_select_set(bm, eve, true);
3031                         }
3032                 }
3033         }
3034
3035         if (em->selectmode & SCE_SELECT_EDGE) {
3036                 BMEdge *eed;
3037                 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
3038                         if (BM_edge_is_wire(eed)) {
3039                                 BM_edge_select_set(bm, eed, true);
3040                         }
3041                 }
3042         }
3043
3044         if (em->selectmode & SCE_SELECT_FACE) {
3045                 BMFace *efa;
3046                 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3047                         BMIter liter;
3048                         BMLoop *l;
3049                         bool is_loose = true;
3050                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3051                                 if (!BM_edge_is_boundary(l->e)) {
3052                                         is_loose = false;
3053                                         break;
3054                                 }
3055                         }
3056                         if (is_loose) {
3057                                 BM_face_select_set(bm, efa, true);
3058                         }
3059                 }
3060         }
3061
3062         EDBM_selectmode_flush(em);
3063
3064         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
3065         return OPERATOR_FINISHED;
3066 }
3067
3068 void MESH_OT_select_loose(wmOperatorType *ot)
3069 {
3070         /* identifiers */
3071         ot->name = "Select Loose Geometry";
3072         ot->description = "Select loose geometry based on the selection mode";
3073         ot->idname = "MESH_OT_select_loose";
3074
3075         /* api callbacks */
3076         ot->exec = edbm_select_loose_exec;
3077         ot->poll = ED_operator_editmesh;
3078
3079         /* flags */
3080         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3081
3082         /* props */
3083         RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
3084 }
3085
3086
3087 static int edbm_select_mirror_exec(bContext *C, wmOperator *op)
3088 {
3089         Object *obedit = CTX_data_edit_object(C);
3090         BMEditMesh *em = BKE_editmesh_from_object(obedit);
3091         const int axis_flag = RNA_enum_get(op->ptr, "axis");
3092         const bool extend = RNA_boolean_get(op->ptr, "extend");
3093
3094         if (em->bm->totvert && em->bm->totvertsel) {
3095                 int totmirr, totfail;
3096
3097                 for (int axis = 0; axis < 3; axis++) {
3098                         if ((1 << axis) & axis_flag) {
3099                                 EDBM_select_mirrored(em, axis, extend, &totmirr, &totfail);
3100                         }
3101                 }
3102
3103                 if (totmirr) {
3104                         EDBM_selectmode_flush(em);
3105                         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
3106                 }
3107
3108                 ED_mesh_report_mirror_ex(op, totmirr, totfail, em->bm->selectmode);
3109         }
3110
3111         return OPERATOR_FINISHED;
3112 }
3113
3114 void MESH_OT_select_mirror(wmOperatorType *ot)
3115 {
3116         /* identifiers */
3117         ot->name = "Select Mirror";
3118         ot->description = "Select mesh items at mirrored locations";
3119         ot->idname = "MESH_OT_select_mirror";
3120
3121         /* api callbacks */
3122         ot->exec = edbm_select_mirror_exec;
3123         ot->poll = ED_operator_editmesh;
3124
3125         /* flags */
3126         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3127
3128         /* props */
3129         RNA_def_enum_flag(ot->srna, "axis", rna_enum_axis_flag_xyz_items, (1 << 0), "Axis", "");
3130
3131         RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the existing selection");
3132 }
3133
3134 /* ******************** **************** */
3135
3136 static int edbm_select_more_exec(bContext *C, wmOperator *op)
3137 {
3138         Object *obedit = CTX_data_edit_object(C);
3139         BMEditMesh *em = BKE_editmesh_from_object(obedit);
3140         const bool use_face_step = RNA_boolean_get(op->ptr, "use_face_step");
3141
3142         EDBM_select_more(em, use_face_step);
3143
3144         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit);
3145         return OPERATOR_FINISHED;
3146 }
3147
3148 void MESH_OT_select_more(wmOperatorType *ot)
3149 {
3150         /* identifiers */
3151         ot->name = "Select More";
3152         ot->idname = "MESH_OT_select_more";
3153         ot->description = "Select more vertices, edges or faces connected to initial selection";
3154
3155         /* api callbacks */
3156         ot->exec = edbm_select_more_exec;
3157         ot->poll = ED_operator_editmesh;
3158         
3159         /* flags */
3160         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3161
3162         RNA_def_boolean(ot->srna, "use_face_step", true, "Face Step", "Connected faces (instead of edges)");
3163 }
3164
3165 static int edbm_select_less_exec(bContext *C, wmOperator *op)
3166 {
3167         Object *obedit = CTX_data_edit_object(C);
3168         BMEditMesh *em = BKE_editmesh_from_object(obedit);
3169         const bool use_face_step = RNA_boolean_get(op->ptr, "use_face_step");
3170
3171         EDBM_select_less(em, use_face_step);
3172
3173         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit);
3174         return OPERATOR_FINISHED;
3175 }
3176
3177 void MESH_OT_select_less(wmOperatorType *ot)
3178 {
3179         /* identifiers */
3180         ot->name = "Select Less";
3181         ot->idname = "MESH_OT_select_less";
3182         ot->description = "Deselect vertices, edges or faces at the boundary of each selection region";
3183
3184         /* api callbacks */
3185         ot->exec = edbm_select_less_exec;
3186         ot->poll = ED_operator_editmesh;
3187         
3188         /* flags */
3189         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3190
3191         RNA_def_boolean(ot->srna, "use_face_step", true, "Face Step", "Connected faces (instead of edges)");
3192 }
3193
3194 /**
3195  * Check if we're connected to another selected efge.
3196  */
3197 static bool bm_edge_is_select_isolated(BMEdge *e)