doxygen: add newline after \file
[blender.git] / source / blender / editors / mesh / editmesh_select_similar.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2004 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup edmesh
22  */
23
24 #include "MEM_guardedalloc.h"
25
26 #include "BLI_kdtree.h"
27 #include "BLI_listbase.h"
28 #include "BLI_math.h"
29
30 #include "BKE_context.h"
31 #include "BKE_editmesh.h"
32 #include "BKE_layer.h"
33 #include "BKE_material.h"
34 #include "BKE_report.h"
35
36 #include "DNA_meshdata_types.h"
37
38 #include "WM_api.h"
39 #include "WM_types.h"
40
41 #include "RNA_access.h"
42 #include "RNA_define.h"
43
44 #include "ED_mesh.h"
45 #include "ED_screen.h"
46 #include "ED_select_utils.h"
47
48 #include "mesh_intern.h"  /* own include */
49
50 /* -------------------------------------------------------------------- */
51 /** \name Select Similar (Vert/Edge/Face) Operator - common
52  * \{ */
53
54 static const EnumPropertyItem prop_similar_compare_types[] = {
55         {SIM_CMP_EQ, "EQUAL", 0, "Equal", ""},
56         {SIM_CMP_GT, "GREATER", 0, "Greater", ""},
57         {SIM_CMP_LT, "LESS", 0, "Less", ""},
58
59         {0, NULL, 0, NULL, NULL},
60 };
61
62 static const EnumPropertyItem prop_similar_types[] = {
63         {SIMVERT_NORMAL, "NORMAL", 0, "Normal", ""},
64         {SIMVERT_FACE, "FACE", 0, "Amount of Adjacent Faces", ""},
65         {SIMVERT_VGROUP, "VGROUP", 0, "Vertex Groups", ""},
66         {SIMVERT_EDGE, "EDGE", 0, "Amount of connecting edges", ""},
67
68         {SIMEDGE_LENGTH, "LENGTH", 0, "Length", ""},
69         {SIMEDGE_DIR, "DIR", 0, "Direction", ""},
70         {SIMEDGE_FACE, "FACE", 0, "Amount of Faces Around an Edge", ""},
71         {SIMEDGE_FACE_ANGLE, "FACE_ANGLE", 0, "Face Angles", ""},
72         {SIMEDGE_CREASE, "CREASE", 0, "Crease", ""},
73         {SIMEDGE_BEVEL, "BEVEL", 0, "Bevel", ""},
74         {SIMEDGE_SEAM, "SEAM", 0, "Seam", ""},
75         {SIMEDGE_SHARP, "SHARP", 0, "Sharpness", ""},
76 #ifdef WITH_FREESTYLE
77         {SIMEDGE_FREESTYLE, "FREESTYLE_EDGE", 0, "Freestyle Edge Marks", ""},
78 #endif
79
80         {SIMFACE_MATERIAL, "MATERIAL", 0, "Material", ""},
81         {SIMFACE_AREA, "AREA", 0, "Area", ""},
82         {SIMFACE_SIDES, "SIDES", 0, "Polygon Sides", ""},
83         {SIMFACE_PERIMETER, "PERIMETER", 0, "Perimeter", ""},
84         {SIMFACE_NORMAL, "NORMAL", 0, "Normal", ""},
85         {SIMFACE_COPLANAR, "COPLANAR", 0, "Co-planar", ""},
86         {SIMFACE_SMOOTH, "SMOOTH", 0, "Flat/Smooth", ""},
87         {SIMFACE_FACEMAP, "FACE_MAP", 0, "Face-Map", ""},
88 #ifdef WITH_FREESTYLE
89         {SIMFACE_FREESTYLE, "FREESTYLE_FACE", 0, "Freestyle Face Marks", ""},
90 #endif
91
92         {0, NULL, 0, NULL, NULL},
93 };
94
95 static int mesh_select_similar_compare_int(const int delta, const int compare)
96 {
97         switch (compare) {
98                 case SIM_CMP_EQ:
99                         return (delta == 0);
100                 case SIM_CMP_GT:
101                         return (delta > 0);
102                 case SIM_CMP_LT:
103                         return (delta < 0);
104                 default:
105                         BLI_assert(0);
106                         return 0;
107         }
108 }
109
110 /** \} */
111
112 /* -------------------------------------------------------------------- */
113 /** \name Select Similar Face
114  * \{ */
115
116 enum {
117         SIMFACE_DATA_NONE  = 0,
118         SIMFACE_DATA_TRUE  = (1 << 0),
119         SIMFACE_DATA_FALSE = (1 << 1),
120         SIMFACE_DATA_ALL   = (SIMFACE_DATA_TRUE | SIMFACE_DATA_FALSE),
121 };
122
123 /**
124  * Return true if we still don't know the final value for this edge data.
125  * In other words, if we need to keep iterating over the objects or we can
126  * just go ahead and select all the objects.
127  */
128 static bool face_data_value_set(BMFace *face, const int hflag, int *r_value)
129 {
130         if (BM_elem_flag_test(face, hflag)) {
131                 *r_value |= SIMFACE_DATA_TRUE;
132         }
133         else {
134                 *r_value |= SIMFACE_DATA_FALSE;
135         }
136
137         return *r_value != SIMFACE_DATA_ALL;
138 }
139
140 /**
141  * Note: This is not normal, but the face direction itself and always in
142  * a positive quadrant (tries z, y then x).
143  * Also, unlike edge_pos_direction_worldspace_get we don't normalize the direction.
144  * In fact we scale the direction by the distance of the face center to the origin.
145  */
146 static void face_pos_direction_worldspace_scaled_get(Object *ob, BMFace *face, float *r_dir)
147 {
148         float distance;
149         float center[3];
150
151         copy_v3_v3(r_dir, face->no);
152         normalize_v3(r_dir);
153
154         BM_face_calc_center_median(face, center);
155         mul_m4_v3(ob->obmat, center);
156
157         distance = dot_v3v3(r_dir, center);
158         mul_v3_fl(r_dir, distance);
159
160         /* Make sure we have a consistent direction regardless of the face orientation.
161          * This spares us from storing dir and -dir in the tree. */
162         if (fabs(r_dir[2]) < FLT_EPSILON) {
163                 if (fabs(r_dir[1]) < FLT_EPSILON) {
164                         if (r_dir[0] < 0.0f) {
165                                 mul_v3_fl(r_dir, -1.0f);
166                         }
167                 }
168                 else if (r_dir[1] < 0.0f) {
169                         mul_v3_fl(r_dir, -1.0f);
170                 }
171         }
172         else if (r_dir[2] < 0.0f) {
173                 mul_v3_fl(r_dir, -1.0f);
174         }
175 }
176
177 /* TODO(dfelinto): `types` that should technically be compared in world space but are not:
178  *  -SIMFACE_AREA
179  *  -SIMFACE_PERIMETER
180  */
181 static int similar_face_select_exec(bContext *C, wmOperator *op)
182 {
183         ViewLayer *view_layer = CTX_data_view_layer(C);
184
185         const int type = RNA_enum_get(op->ptr, "type");
186         const float thresh = RNA_float_get(op->ptr, "threshold");
187         const float thresh_radians = thresh * (float)M_PI;
188         const int compare = RNA_enum_get(op->ptr, "compare");
189
190         int tot_faces_selected_all = 0;
191         uint objects_len = 0;
192         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
193
194         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
195                 Object *ob = objects[ob_index];
196                 BMEditMesh *em = BKE_editmesh_from_object(ob);
197                 tot_faces_selected_all += em->bm->totfacesel;
198         }
199
200         if (tot_faces_selected_all == 0) {
201                 BKE_report(op->reports, RPT_ERROR, "No face selected");
202                 MEM_freeN(objects);
203                 return OPERATOR_CANCELLED;
204         }
205
206         KDTree *tree = NULL;
207         GSet *gset = NULL;
208         GSet **gset_array = NULL;
209         int face_data_value = SIMFACE_DATA_NONE;
210
211         switch (type) {
212                 case SIMFACE_AREA:
213                 case SIMFACE_PERIMETER:
214                 case SIMFACE_NORMAL:
215                 case SIMFACE_COPLANAR:
216                         tree = BLI_kdtree_new(tot_faces_selected_all);
217                         break;
218                 case SIMFACE_SIDES:
219                 case SIMFACE_MATERIAL:
220                         gset = BLI_gset_ptr_new("Select similar face");
221                         break;
222                 case SIMFACE_FACEMAP:
223                         gset_array = MEM_callocN(sizeof(GSet *) * objects_len, "Select similar face: facemap gset array");
224                         break;
225         }
226
227         int tree_index = 0;
228         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
229                 Object *ob = objects[ob_index];
230                 BMEditMesh *em = BKE_editmesh_from_object(ob);
231                 BMesh *bm = em->bm;
232                 Material ***material_array = NULL;
233                 invert_m4_m4(ob->imat, ob->obmat);
234                 int custom_data_offset = 0;
235
236                 if (bm->totfacesel == 0) {
237                         continue;
238                 }
239
240                 float ob_m3[3][3];
241                 copy_m3_m4(ob_m3, ob->obmat);
242
243                 switch (type) {
244                         case SIMFACE_MATERIAL:
245                         {
246                                 if (ob->totcol == 0) {
247                                         continue;
248                                 }
249                                 material_array = give_matarar(ob);
250                                 break;
251                         }
252                         case SIMFACE_FREESTYLE:
253                         {
254                                 if (!CustomData_has_layer(&bm->pdata, CD_FREESTYLE_FACE)) {
255                                         face_data_value |= SIMFACE_DATA_FALSE;
256                                         continue;
257                                 }
258                                 break;
259                         }
260                         case SIMFACE_FACEMAP:
261                         {
262                                 custom_data_offset = CustomData_get_offset(&bm->pdata, CD_FACEMAP);
263                                 if (custom_data_offset == -1) {
264                                         continue;
265                                 }
266                                 else {
267                                         gset_array[ob_index] = BLI_gset_ptr_new("Select similar face: facemap gset");
268                                 }
269                         }
270                 }
271
272                 BMFace *face; /* Mesh face. */
273                 BMIter iter; /* Selected faces iterator. */
274
275                 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
276                         if (BM_elem_flag_test(face, BM_ELEM_SELECT)) {
277                                 switch (type) {
278                                         case SIMFACE_SIDES:
279                                                 BLI_gset_add(gset, POINTER_FROM_INT(face->len));
280                                                 break;
281                                         case SIMFACE_MATERIAL:
282                                         {
283                                                 Material *material = (*material_array)[face->mat_nr];
284                                                 if (material != NULL) {
285                                                         BLI_gset_add(gset, material);
286                                                 }
287                                                 break;
288                                         }
289                                         case SIMFACE_AREA:
290                                         {
291                                                 float area = BM_face_calc_area_with_mat3(face, ob_m3);
292                                                 float dummy[3] = {area, 0.0f, 0.0f};
293                                                 BLI_kdtree_insert(tree, tree_index++, dummy);
294                                                 break;
295                                         }
296                                         case SIMFACE_PERIMETER:
297                                         {
298                                                 float perimeter = BM_face_calc_perimeter_with_mat3(face, ob_m3);
299                                                 float dummy[3] = {perimeter, 0.0f, 0.0f};
300                                                 BLI_kdtree_insert(tree, tree_index++, dummy);
301                                                 break;
302                                         }
303                                         case SIMFACE_NORMAL:
304                                         {
305                                                 float normal[3];
306                                                 copy_v3_v3(normal, face->no);
307                                                 mul_transposed_mat3_m4_v3(ob->imat, normal);
308                                                 normalize_v3(normal);
309
310                                                 BLI_kdtree_insert(tree, tree_index++, normal);
311                                                 break;
312                                         }
313                                         case SIMFACE_COPLANAR:
314                                         {
315                                                 float dir[3];
316                                                 face_pos_direction_worldspace_scaled_get(ob, face, dir);
317                                                 BLI_kdtree_insert(tree, tree_index++, dir);
318                                                 break;
319                                         }
320                                         case SIMFACE_SMOOTH:
321                                         {
322                                                 if (!face_data_value_set(face, BM_ELEM_SMOOTH, &face_data_value)) {
323                                                         goto face_select_all;
324                                                 }
325                                                 break;
326                                         }
327                                         case SIMFACE_FREESTYLE:
328                                         {
329                                                 FreestyleFace *fface;
330                                                 fface = CustomData_bmesh_get(&bm->pdata, face->head.data, CD_FREESTYLE_FACE);
331                                                 if ((fface == NULL) || ((fface->flag & FREESTYLE_FACE_MARK) == 0)) {
332                                                         face_data_value |= SIMFACE_DATA_FALSE;
333                                                 }
334                                                 else {
335                                                         face_data_value |= SIMFACE_DATA_TRUE;
336                                                 }
337                                                 if (face_data_value == SIMFACE_DATA_ALL) {
338                                                         goto face_select_all;
339                                                 }
340                                                 break;
341                                         }
342                                         case SIMFACE_FACEMAP:
343                                         {
344                                                 BLI_assert(custom_data_offset != -1);
345                                                 int *face_map = BM_ELEM_CD_GET_VOID_P(face, custom_data_offset);
346                                                 BLI_gset_add(gset_array[ob_index], face_map);
347                                                 break;
348                                         }
349                                 }
350                         }
351                 }
352         }
353
354         BLI_assert((type != SIMFACE_FREESTYLE) || (face_data_value != SIMFACE_DATA_NONE));
355
356         if (tree != NULL) {
357                 BLI_kdtree_balance(tree);
358         }
359
360         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
361                 Object *ob = objects[ob_index];
362                 BMEditMesh *em = BKE_editmesh_from_object(ob);
363                 BMesh *bm = em->bm;
364                 bool changed = false;
365                 Material ***material_array = NULL;
366                 int custom_data_offset;
367
368                 float ob_m3[3][3];
369                 copy_m3_m4(ob_m3, ob->obmat);
370
371                 bool has_custom_data_layer = false;
372                 switch (type) {
373                         case SIMFACE_MATERIAL:
374                         {
375                                 if (ob->totcol == 0) {
376                                         continue;
377                                 }
378                                 material_array = give_matarar(ob);
379                                 break;
380                         }
381                         case SIMFACE_FREESTYLE:
382                         {
383                                 has_custom_data_layer = CustomData_has_layer(&bm->pdata, CD_FREESTYLE_FACE);
384                                 if ((face_data_value == SIMFACE_DATA_TRUE) && !has_custom_data_layer) {
385                                         continue;
386                                 }
387                                 break;
388                         }
389                         case SIMFACE_FACEMAP:
390                         {
391                                 custom_data_offset = CustomData_get_offset(&bm->pdata, CD_FACEMAP);
392                                 if (custom_data_offset == -1) {
393                                         continue;
394                                 }
395                         }
396                 }
397
398                 BMFace *face; /* Mesh face. */
399                 BMIter iter; /* Selected faces iterator. */
400
401                 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
402                         if (!BM_elem_flag_test(face, BM_ELEM_SELECT) &&
403                             !BM_elem_flag_test(face, BM_ELEM_HIDDEN))
404                         {
405                                 bool select = false;
406                                 switch (type) {
407                                         case SIMFACE_SIDES:
408                                         {
409                                                 const int num_sides = face->len;
410                                                 GSetIterator gs_iter;
411                                                 GSET_ITER(gs_iter, gset) {
412                                                         const int num_sides_iter = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
413                                                         const int delta_i = num_sides - num_sides_iter;
414                                                         if (mesh_select_similar_compare_int(delta_i, compare)) {
415                                                                 select = true;
416                                                                 break;
417                                                         }
418                                                 }
419                                                 break;
420                                         }
421                                         case SIMFACE_MATERIAL:
422                                         {
423                                                 const Material *material = (*material_array)[face->mat_nr];
424                                                 if (material == NULL) {
425                                                         continue;
426                                                 }
427
428                                                 GSetIterator gs_iter;
429                                                 GSET_ITER(gs_iter, gset) {
430                                                         const Material *material_iter = BLI_gsetIterator_getKey(&gs_iter);
431                                                         if (material == material_iter) {
432                                                                 select = true;
433                                                                 break;
434                                                         }
435                                                 }
436                                                 break;
437                                         }
438                                         case SIMFACE_AREA:
439                                         {
440                                                 float area = BM_face_calc_area_with_mat3(face, ob_m3);
441                                                 if (ED_select_similar_compare_float_tree(tree, area, thresh, compare)) {
442                                                         select = true;
443                                                 }
444                                                 break;
445                                         }
446                                         case SIMFACE_PERIMETER:
447                                         {
448                                                 float perimeter = BM_face_calc_perimeter_with_mat3(face, ob_m3);
449                                                 if (ED_select_similar_compare_float_tree(tree, perimeter, thresh, compare)) {
450                                                         select = true;
451                                                 }
452                                                 break;
453                                         }
454                                         case SIMFACE_NORMAL:
455                                         {
456                                                 float normal[3];
457                                                 copy_v3_v3(normal, face->no);
458                                                 mul_transposed_mat3_m4_v3(ob->imat, normal);
459                                                 normalize_v3(normal);
460
461                                                 /* We are treating the normals as coordinates, the "nearest" one will
462                                                  * also be the one closest to the angle. */
463                                                 KDTreeNearest nearest;
464                                                 if (BLI_kdtree_find_nearest(tree, normal, &nearest) != -1) {
465                                                         if (angle_normalized_v3v3(normal, nearest.co) <= thresh_radians) {
466                                                                 select = true;
467                                                         }
468                                                 }
469                                                 break;
470                                         }
471                                         case SIMFACE_COPLANAR:
472                                         {
473                                                 float diff[3];
474                                                 float dir[3];
475                                                 face_pos_direction_worldspace_scaled_get(ob, face, dir);
476
477                                                 /* We are treating the direction as coordinates, the "nearest" one will
478                                                  * also be the one closest to the angle.
479                                                  * And since the direction is scaled by the face center distance to the origin,
480                                                  * the nearest point will also be the closest between the planes. */
481                                                 KDTreeNearest nearest;
482                                                 if (BLI_kdtree_find_nearest(tree, dir, &nearest) != -1) {
483                                                         sub_v3_v3v3(diff, dir, nearest.co);
484                                                         if (len_v3(diff) <= thresh) {
485                                                                 if (angle_v3v3(dir, nearest.co) <= thresh_radians) {
486                                                                         select = true;
487                                                                 }
488                                                         }
489                                                 }
490                                                 break;
491                                         }
492                                         case SIMFACE_SMOOTH:
493                                                 if ((BM_elem_flag_test(face, BM_ELEM_SMOOTH) != 0) ==
494                                                     ((face_data_value & SIMFACE_DATA_TRUE) != 0))
495                                                 {
496                                                         select = true;
497                                                 }
498                                                 break;
499                                         case SIMFACE_FREESTYLE:
500                                         {
501                                                 FreestyleFace *fface;
502
503                                                 if (!has_custom_data_layer) {
504                                                         BLI_assert(face_data_value == SIMFACE_DATA_FALSE);
505                                                         select = true;
506                                                         break;
507                                                 }
508
509                                                 fface = CustomData_bmesh_get(&bm->pdata, face->head.data, CD_FREESTYLE_FACE);
510                                                 if (((fface != NULL) && (fface->flag & FREESTYLE_FACE_MARK)) ==
511                                                     ((face_data_value & SIMFACE_DATA_TRUE) != 0))
512                                                 {
513                                                         select = true;
514                                                 }
515                                                 break;
516                                         }
517                                         case SIMFACE_FACEMAP:
518                                         {
519                                                 const int *face_map = BM_ELEM_CD_GET_VOID_P(face, custom_data_offset);
520                                                 GSetIterator gs_iter;
521                                                 GSET_ITER(gs_iter, gset_array[ob_index]) {
522                                                         const int *face_map_iter = BLI_gsetIterator_getKey(&gs_iter);
523                                                         if (*face_map == *face_map_iter) {
524                                                                 select = true;
525                                                                 break;
526                                                         }
527                                                 }
528                                                 break;
529                                         }
530                                 }
531
532                                 if (select) {
533                                         BM_face_select_set(bm, face, true);
534                                         changed = true;
535                                 }
536                         }
537                 }
538
539                 if (changed) {
540                         EDBM_selectmode_flush(em);
541                         EDBM_update_generic(em, false, false);
542                 }
543         }
544
545         if (false) {
546 face_select_all:
547                 BLI_assert(ELEM(type,
548                                 SIMFACE_SMOOTH,
549                                 SIMFACE_FREESTYLE
550                                 ));
551
552                 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
553                         Object *ob = objects[ob_index];
554                         BMEditMesh *em = BKE_editmesh_from_object(ob);
555                         BMesh *bm = em->bm;
556
557                         BMFace *face; /* Mesh face. */
558                         BMIter iter; /* Selected faces iterator. */
559
560                         BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
561                                 if (!BM_elem_flag_test(face, BM_ELEM_SELECT)) {
562                                         BM_face_select_set(bm, face, true);
563                                 }
564                         }
565                         EDBM_selectmode_flush(em);
566                         EDBM_update_generic(em, false, false);
567                 }
568         }
569
570         MEM_freeN(objects);
571         BLI_kdtree_free(tree);
572         if (gset != NULL) {
573                 BLI_gset_free(gset, NULL);
574         }
575         if (gset_array != NULL) {
576                 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
577                         if (gset_array[ob_index] != NULL) {
578                                 BLI_gset_free(gset_array[ob_index], NULL);
579                         }
580                 }
581                 MEM_freeN(gset_array);
582         }
583
584         return OPERATOR_FINISHED;
585 }
586
587 /** \} */
588
589 /* -------------------------------------------------------------------- */
590 /** \name Select Similar Edge
591  * \{ */
592
593
594 /**
595  * Note: This is not normal, but the edge direction itself and always in
596  * a positive quadrant (tries z, y then x).
597  * Therefore we need to use the entire object transformation matrix.
598  */
599 static void edge_pos_direction_worldspace_get(Object *ob, BMEdge *edge, float *r_dir)
600 {
601         float v1[3], v2[3];
602         copy_v3_v3(v1, edge->v1->co);
603         copy_v3_v3(v2, edge->v2->co);
604
605         mul_m4_v3(ob->obmat, v1);
606         mul_m4_v3(ob->obmat, v2);
607
608         sub_v3_v3v3(r_dir, v1, v2);
609         normalize_v3(r_dir);
610
611         /* Make sure we have a consistent direction that can be checked regardless of
612          * the verts order of the edges. This spares us from storing dir and -dir in the tree. */
613         if (fabs(r_dir[2]) < FLT_EPSILON) {
614                 if (fabs(r_dir[1]) < FLT_EPSILON) {
615                         if (r_dir[0] < 0.0f) {
616                                 mul_v3_fl(r_dir, -1.0f);
617                         }
618                 }
619                 else if (r_dir[1] < 0.0f) {
620                         mul_v3_fl(r_dir, -1.0f);
621                 }
622         }
623         else if (r_dir[2] < 0.0f) {
624                 mul_v3_fl(r_dir, -1.0f);
625         }
626 }
627
628 static float edge_length_squared_worldspace_get(Object *ob, BMEdge *edge)
629 {
630         float v1[3], v2[3];
631
632         mul_v3_mat3_m4v3(v1, ob->obmat, edge->v1->co);
633         mul_v3_mat3_m4v3(v2, ob->obmat, edge->v2->co);
634
635         return len_squared_v3v3(v1, v2);
636 }
637
638 enum {
639         SIMEDGE_DATA_NONE  = 0,
640         SIMEDGE_DATA_TRUE  = (1 << 0),
641         SIMEDGE_DATA_FALSE = (1 << 1),
642         SIMEDGE_DATA_ALL   = (SIMEDGE_DATA_TRUE | SIMEDGE_DATA_FALSE),
643 };
644
645 /**
646  * Return true if we still don't know the final value for this edge data.
647  * In other words, if we need to keep iterating over the objects or we can
648  * just go ahead and select all the objects.
649  */
650 static bool edge_data_value_set(BMEdge *edge, const int hflag, int *r_value)
651 {
652         if (BM_elem_flag_test(edge, hflag)) {
653                 *r_value |= SIMEDGE_DATA_TRUE;
654         }
655         else {
656                 *r_value |= SIMEDGE_DATA_FALSE;
657         }
658
659         return *r_value != SIMEDGE_DATA_ALL;
660 }
661
662 /* TODO(dfelinto): `types` that should technically be compared in world space but are not:
663  *  -SIMEDGE_FACE_ANGLE
664  */
665 static int similar_edge_select_exec(bContext *C, wmOperator *op)
666 {
667         ViewLayer *view_layer = CTX_data_view_layer(C);
668
669         const int type = RNA_enum_get(op->ptr, "type");
670         const float thresh = RNA_float_get(op->ptr, "threshold");
671         const float thresh_radians = thresh * (float)M_PI + FLT_EPSILON;
672         const int compare = RNA_enum_get(op->ptr, "compare");
673         int custom_data_type = -1;
674
675         int tot_edges_selected_all = 0;
676         uint objects_len = 0;
677         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
678
679         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
680                 Object *ob = objects[ob_index];
681                 BMEditMesh *em = BKE_editmesh_from_object(ob);
682                 tot_edges_selected_all += em->bm->totedgesel;
683         }
684
685         if (tot_edges_selected_all == 0) {
686                 BKE_report(op->reports, RPT_ERROR, "No edge selected");
687                 MEM_freeN(objects);
688                 return OPERATOR_CANCELLED;
689         }
690
691         KDTree *tree = NULL;
692         GSet *gset = NULL;
693         int edge_data_value = SIMEDGE_DATA_NONE;
694
695         switch (type) {
696                 case SIMEDGE_CREASE:
697                 case SIMEDGE_BEVEL:
698                 case SIMEDGE_FACE_ANGLE:
699                 case SIMEDGE_LENGTH:
700                 case SIMEDGE_DIR:
701                         tree = BLI_kdtree_new(tot_edges_selected_all);
702                         break;
703                 case SIMEDGE_FACE:
704                         gset = BLI_gset_ptr_new("Select similar edge: face");
705                         break;
706         }
707
708         switch (type) {
709                 case SIMEDGE_CREASE:
710                         custom_data_type = CD_CREASE;
711                         break;
712                 case SIMEDGE_BEVEL:
713                         custom_data_type = CD_BWEIGHT;
714                         break;
715         }
716
717         int tree_index = 0;
718         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
719                 Object *ob = objects[ob_index];
720                 BMEditMesh *em = BKE_editmesh_from_object(ob);
721                 BMesh *bm = em->bm;
722
723                 if (bm->totedgesel == 0) {
724                         continue;
725                 }
726
727                 switch (type) {
728                         case SIMEDGE_FREESTYLE:
729                         {
730                                 if (!CustomData_has_layer(&bm->edata, CD_FREESTYLE_EDGE)) {
731                                         edge_data_value |= SIMEDGE_DATA_FALSE;
732                                         continue;
733                                 }
734                                 break;
735                         }
736                         case SIMEDGE_CREASE:
737                         case SIMEDGE_BEVEL:
738                         {
739                                 if (!CustomData_has_layer(&bm->edata, custom_data_type)) {
740                                         float dummy[3] = {0.0f, 0.0f, 0.0f};
741                                         BLI_kdtree_insert(tree, tree_index++, dummy);
742                                         continue;
743                                 }
744                                 break;
745                         }
746                 }
747
748                 float ob_m3[3][3], ob_m3_inv[3][3];
749                 copy_m3_m4(ob_m3, ob->obmat);
750                 invert_m3_m3(ob_m3_inv, ob_m3);
751
752                 BMEdge *edge; /* Mesh edge. */
753                 BMIter iter; /* Selected edges iterator. */
754
755                 BM_ITER_MESH (edge, &iter, bm, BM_EDGES_OF_MESH) {
756                         if (BM_elem_flag_test(edge, BM_ELEM_SELECT)) {
757                                 switch (type) {
758                                         case SIMEDGE_FACE:
759                                                 BLI_gset_add(gset, POINTER_FROM_INT(BM_edge_face_count(edge)));
760                                                 break;
761                                         case SIMEDGE_DIR:
762                                         {
763                                                 float dir[3];
764                                                 edge_pos_direction_worldspace_get(ob, edge, dir);
765                                                 BLI_kdtree_insert(tree, tree_index++, dir);
766                                                 break;
767                                         }
768                                         case SIMEDGE_LENGTH:
769                                         {
770                                                 float length = edge_length_squared_worldspace_get(ob, edge);
771                                                 float dummy[3] = {length, 0.0f, 0.0f};
772                                                 BLI_kdtree_insert(tree, tree_index++, dummy);
773                                                 break;
774                                         }
775                                         case SIMEDGE_FACE_ANGLE:
776                                         {
777                                                 if (BM_edge_face_count_at_most(edge, 2) == 2) {
778                                                         float angle = BM_edge_calc_face_angle_with_imat3(edge, ob_m3_inv);
779                                                         float dummy[3] = {angle, 0.0f, 0.0f};
780                                                         BLI_kdtree_insert(tree, tree_index++, dummy);
781                                                 }
782                                                 break;
783                                         }
784                                         case SIMEDGE_SEAM:
785                                                 if (!edge_data_value_set(edge, BM_ELEM_SEAM, &edge_data_value)) {
786                                                         goto edge_select_all;
787                                                 }
788                                                 break;
789                                         case SIMEDGE_SHARP:
790                                                 if (!edge_data_value_set(edge, BM_ELEM_SMOOTH, &edge_data_value)) {
791                                                         goto edge_select_all;
792                                                 }
793                                                 break;
794                                         case SIMEDGE_FREESTYLE:
795                                         {
796                                                 FreestyleEdge *fedge;
797                                                 fedge = CustomData_bmesh_get(&bm->edata, edge->head.data, CD_FREESTYLE_EDGE);
798                                                 if ((fedge == NULL) || ((fedge->flag & FREESTYLE_EDGE_MARK) == 0)) {
799                                                         edge_data_value |= SIMEDGE_DATA_FALSE;
800                                                 }
801                                                 else {
802                                                         edge_data_value |= SIMEDGE_DATA_TRUE;
803                                                 }
804                                                 if (edge_data_value == SIMEDGE_DATA_ALL) {
805                                                         goto edge_select_all;
806                                                 }
807                                                 break;
808                                         }
809                                         case SIMEDGE_CREASE:
810                                         case SIMEDGE_BEVEL:
811                                         {
812                                                 const float *value = CustomData_bmesh_get(&bm->edata, edge->head.data, custom_data_type);
813                                                 float dummy[3] = {*value, 0.0f, 0.0f};
814                                                 BLI_kdtree_insert(tree, tree_index++, dummy);
815                                                 break;
816                                         }
817                                 }
818                         }
819                 }
820         }
821
822         BLI_assert((type != SIMEDGE_FREESTYLE) || (edge_data_value != SIMEDGE_DATA_NONE));
823
824         if (tree != NULL) {
825                 BLI_kdtree_balance(tree);
826         }
827
828         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
829                 Object *ob = objects[ob_index];
830                 BMEditMesh *em = BKE_editmesh_from_object(ob);
831                 BMesh *bm = em->bm;
832                 bool changed = false;
833
834                 bool has_custom_data_layer = false;
835                 switch (type) {
836                         case SIMEDGE_FREESTYLE:
837                         {
838                                 has_custom_data_layer = CustomData_has_layer(&bm->edata, CD_FREESTYLE_EDGE);
839                                 if ((edge_data_value == SIMEDGE_DATA_TRUE) && !has_custom_data_layer) {
840                                         continue;
841                                 }
842                                 break;
843                         }
844                         case SIMEDGE_CREASE:
845                         case SIMEDGE_BEVEL:
846                         {
847                                 has_custom_data_layer = CustomData_has_layer(&bm->edata, custom_data_type);
848                                 if (!has_custom_data_layer) {
849                                         /* Proceed only if we have to select all the edges that have custom data value of 0.0f.
850                                          * In this case we will just select all the edges.
851                                          * Otherwise continue the for loop. */
852                                         if (!ED_select_similar_compare_float_tree(tree, 0.0f, thresh, compare)) {
853                                                 continue;
854                                         }
855                                 }
856                         }
857                 }
858
859                 float ob_m3[3][3], ob_m3_inv[3][3];
860                 copy_m3_m4(ob_m3, ob->obmat);
861                 invert_m3_m3(ob_m3_inv, ob_m3);
862
863                 BMEdge *edge; /* Mesh edge. */
864                 BMIter iter; /* Selected edges iterator. */
865
866                 BM_ITER_MESH (edge, &iter, bm, BM_EDGES_OF_MESH) {
867                         if (!BM_elem_flag_test(edge, BM_ELEM_SELECT) &&
868                             !BM_elem_flag_test(edge, BM_ELEM_HIDDEN))
869                         {
870                                 bool select = false;
871                                 switch (type) {
872                                         case SIMEDGE_FACE:
873                                         {
874                                                 const int num_faces = BM_edge_face_count(edge);
875                                                 GSetIterator gs_iter;
876                                                 GSET_ITER(gs_iter, gset) {
877                                                         const int num_faces_iter = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
878                                                         const int delta_i = num_faces - num_faces_iter;
879                                                         if (mesh_select_similar_compare_int(delta_i, compare)) {
880                                                                 select = true;
881                                                                 break;
882                                                         }
883                                                 }
884                                                 break;
885                                         }
886                                         case SIMEDGE_DIR:
887                                         {
888                                                 float dir[3];
889                                                 edge_pos_direction_worldspace_get(ob, edge, dir);
890
891                                                 /* We are treating the direction as coordinates, the "nearest" one will
892                                                  * also be the one closest to the intended direction. */
893                                                 KDTreeNearest nearest;
894                                                 if (BLI_kdtree_find_nearest(tree, dir, &nearest) != -1) {
895                                                         if (angle_normalized_v3v3(dir, nearest.co) <= thresh_radians) {
896                                                                 select = true;
897                                                         }
898                                                 }
899                                                 break;
900                                         }
901                                         case SIMEDGE_LENGTH:
902                                         {
903                                                 float length = edge_length_squared_worldspace_get(ob, edge);
904                                                 if (ED_select_similar_compare_float_tree(tree, length, thresh, compare)) {
905                                                         select = true;
906                                                 }
907                                                 break;
908                                         }
909                                         case SIMEDGE_FACE_ANGLE:
910                                         {
911                                                 if (BM_edge_face_count_at_most(edge, 2) == 2) {
912                                                         float angle = BM_edge_calc_face_angle_with_imat3(edge, ob_m3_inv);
913                                                         if (ED_select_similar_compare_float_tree(tree, angle, thresh, SIM_CMP_EQ)) {
914                                                                 select = true;
915                                                         }
916                                                 }
917                                                 break;
918                                         }
919                                         case SIMEDGE_SEAM:
920                                                 if ((BM_elem_flag_test(edge, BM_ELEM_SEAM) != 0) ==
921                                                     ((edge_data_value & SIMEDGE_DATA_TRUE) != 0))
922                                                 {
923                                                         select = true;
924                                                 }
925                                                 break;
926                                         case SIMEDGE_SHARP:
927                                                 if ((BM_elem_flag_test(edge, BM_ELEM_SMOOTH) != 0) ==
928                                                     ((edge_data_value & SIMEDGE_DATA_TRUE) != 0))
929                                                 {
930                                                         select = true;
931                                                 }
932                                                 break;
933                                         case SIMEDGE_FREESTYLE:
934                                         {
935                                                 FreestyleEdge *fedge;
936
937                                                 if (!has_custom_data_layer) {
938                                                         BLI_assert(edge_data_value == SIMEDGE_DATA_FALSE);
939                                                         select = true;
940                                                         break;
941                                                 }
942
943                                                 fedge = CustomData_bmesh_get(&bm->edata, edge->head.data, CD_FREESTYLE_EDGE);
944                                                 if (((fedge != NULL) && (fedge->flag & FREESTYLE_EDGE_MARK)) ==
945                                                     ((edge_data_value & SIMEDGE_DATA_TRUE) != 0))
946                                                 {
947                                                         select = true;
948                                                 }
949                                                 break;
950                                         }
951                                         case SIMEDGE_CREASE:
952                                         case SIMEDGE_BEVEL:
953                                         {
954                                                 if (!has_custom_data_layer) {
955                                                         select = true;
956                                                         break;
957                                                 }
958
959                                                 const float *value = CustomData_bmesh_get(&bm->edata, edge->head.data, custom_data_type);
960                                                 if (ED_select_similar_compare_float_tree(tree, *value, thresh, compare)) {
961                                                         select = true;
962                                                 }
963                                                 break;
964                                         }
965                                 }
966
967                                 if (select) {
968                                         BM_edge_select_set(bm, edge, true);
969                                         changed = true;
970                                 }
971                         }
972                 }
973
974                 if (changed) {
975                         EDBM_selectmode_flush(em);
976                         EDBM_update_generic(em, false, false);
977                 }
978         }
979
980         if (false) {
981 edge_select_all:
982                 BLI_assert(ELEM(type,
983                                 SIMEDGE_SEAM,
984                                 SIMEDGE_SHARP,
985                                 SIMEDGE_FREESTYLE
986                                 ));
987
988                 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
989                         Object *ob = objects[ob_index];
990                         BMEditMesh *em = BKE_editmesh_from_object(ob);
991                         BMesh *bm = em->bm;
992
993                         BMEdge *edge; /* Mesh edge. */
994                         BMIter iter; /* Selected edges iterator. */
995
996                         BM_ITER_MESH (edge, &iter, bm, BM_EDGES_OF_MESH) {
997                                 if (!BM_elem_flag_test(edge, BM_ELEM_SELECT)) {
998                                         BM_edge_select_set(bm, edge, true);
999                                 }
1000                         }
1001                         EDBM_selectmode_flush(em);
1002                         EDBM_update_generic(em, false, false);
1003                 }
1004         }
1005
1006         MEM_freeN(objects);
1007         BLI_kdtree_free(tree);
1008         if (gset != NULL) {
1009                 BLI_gset_free(gset, NULL);
1010         }
1011
1012         return OPERATOR_FINISHED;
1013 }
1014 /** \} */
1015
1016 /* -------------------------------------------------------------------- */
1017 /** \name Select Similar Vert
1018  * \{ */
1019
1020 static int similar_vert_select_exec(bContext *C, wmOperator *op)
1021 {
1022         ViewLayer *view_layer = CTX_data_view_layer(C);
1023
1024         /* get the type from RNA */
1025         const int type = RNA_enum_get(op->ptr, "type");
1026         const float thresh = RNA_float_get(op->ptr, "threshold");
1027         const float thresh_radians = thresh * (float)M_PI + FLT_EPSILON;
1028         const int compare = RNA_enum_get(op->ptr, "compare");
1029
1030         int tot_verts_selected_all = 0;
1031         uint objects_len = 0;
1032         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
1033
1034         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1035                 Object *ob = objects[ob_index];
1036                 BMEditMesh *em = BKE_editmesh_from_object(ob);
1037                 tot_verts_selected_all += em->bm->totvertsel;
1038         }
1039
1040         if (tot_verts_selected_all == 0) {
1041                 BKE_report(op->reports, RPT_ERROR, "No vertex selected");
1042                 MEM_freeN(objects);
1043                 return OPERATOR_CANCELLED;
1044         }
1045
1046         KDTree *tree = NULL;
1047         GSet *gset = NULL;
1048
1049         switch (type) {
1050                 case SIMVERT_NORMAL:
1051                         tree = BLI_kdtree_new(tot_verts_selected_all);
1052                         break;
1053                 case SIMVERT_EDGE:
1054                 case SIMVERT_FACE:
1055                         gset = BLI_gset_ptr_new("Select similar vertex: edge/face");
1056                         break;
1057                 case SIMVERT_VGROUP:
1058                         gset = BLI_gset_str_new("Select similar vertex: vertex groups");
1059                         break;
1060         }
1061
1062         int normal_tree_index = 0;
1063         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1064                 Object *ob = objects[ob_index];
1065                 BMEditMesh *em = BKE_editmesh_from_object(ob);
1066                 BMesh *bm = em->bm;
1067                 int cd_dvert_offset = -1;
1068                 int dvert_selected = 0;
1069                 invert_m4_m4(ob->imat, ob->obmat);
1070
1071                 if (bm->totvertsel == 0) {
1072                         continue;
1073                 }
1074
1075                 if (type == SIMVERT_VGROUP) {
1076                         cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT);
1077                         if (cd_dvert_offset == -1) {
1078                                 continue;
1079                         }
1080                 }
1081
1082                 BMVert *vert; /* Mesh vertex. */
1083                 BMIter iter; /* Selected verts iterator. */
1084
1085                 BM_ITER_MESH (vert, &iter, bm, BM_VERTS_OF_MESH) {
1086                         if (BM_elem_flag_test(vert, BM_ELEM_SELECT)) {
1087                                 switch (type) {
1088                                         case SIMVERT_FACE:
1089                                                 BLI_gset_add(gset, POINTER_FROM_INT(BM_vert_face_count(vert)));
1090                                                 break;
1091                                         case SIMVERT_EDGE:
1092                                                 BLI_gset_add(gset, POINTER_FROM_INT(BM_vert_edge_count(vert)));
1093                                                 break;
1094                                         case SIMVERT_NORMAL:
1095                                         {
1096                                                 float normal[3];
1097                                                 copy_v3_v3(normal, vert->no);
1098                                                 mul_transposed_mat3_m4_v3(ob->imat, normal);
1099                                                 normalize_v3(normal);
1100
1101                                                 BLI_kdtree_insert(tree, normal_tree_index++, normal);
1102                                                 break;
1103                                         }
1104                                         case SIMVERT_VGROUP:
1105                                         {
1106                                                 MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(vert, cd_dvert_offset);
1107                                                 MDeformWeight *dw = dvert->dw;
1108
1109                                                 for (int i = 0; i < dvert->totweight; i++, dw++) {
1110                                                         if (dw->weight > 0.0f) {
1111                                                                 dvert_selected |= (1 << dw->def_nr);
1112                                                         }
1113                                                 }
1114                                                 break;
1115                                         }
1116                                 }
1117                         }
1118                 }
1119
1120                 if (type == SIMVERT_VGROUP) {
1121                         /* We store the names of the vertex groups, so we can select
1122                          * vertex groups with the same name in  different objects. */
1123                         const int dvert_tot = BLI_listbase_count(&ob->defbase);
1124                         for (int i = 0; i < dvert_tot; i++) {
1125                                 if (dvert_selected & (1 << i)) {
1126                                         bDeformGroup *dg = BLI_findlink(&ob->defbase, i);
1127                                         BLI_gset_add(gset, dg->name);
1128                                 }
1129                         }
1130                 }
1131         }
1132
1133         if (type == SIMVERT_VGROUP) {
1134                 if (BLI_gset_len(gset) == 0) {
1135                         BKE_report(op->reports,
1136                                    RPT_INFO,
1137                                    "No vertex group among the selected vertices");
1138                 }
1139         }
1140
1141         /* Remove duplicated entries. */
1142         if (tree != NULL) {
1143                 BLI_kdtree_balance(tree);
1144         }
1145
1146         /* Run .the BM operators. */
1147         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1148                 Object *ob = objects[ob_index];
1149                 BMEditMesh *em = BKE_editmesh_from_object(ob);
1150                 BMesh *bm = em->bm;
1151                 bool changed = false;
1152                 int cd_dvert_offset = -1;
1153                 int dvert_selected = 0;
1154
1155                 if (type == SIMVERT_VGROUP) {
1156                         cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT);
1157                         if (cd_dvert_offset == -1) {
1158                                 continue;
1159                         }
1160
1161                         /* We map back the names of the vertex groups to their corresponsing indices
1162                          * for this object. This is fast, and keep the logic for each vertex very simple. */
1163                         GSetIterator gs_iter;
1164                         GSET_ITER(gs_iter, gset) {
1165                                 const char *name = BLI_gsetIterator_getKey(&gs_iter);
1166                                 int vgroup_id = BLI_findstringindex(&ob->defbase,
1167                                                                     name,
1168                                                                     offsetof(bDeformGroup, name));
1169                                 if (vgroup_id != -1) {
1170                                         dvert_selected |= (1 << vgroup_id);
1171                                 }
1172                         }
1173                         if (dvert_selected == 0) {
1174                                 continue;
1175                         }
1176                 }
1177
1178                 BMVert *vert; /* Mesh vertex. */
1179                 BMIter iter; /* Selected verts iterator. */
1180
1181                 BM_ITER_MESH (vert, &iter, bm, BM_VERTS_OF_MESH) {
1182                         if (!BM_elem_flag_test(vert, BM_ELEM_SELECT) &&
1183                             !BM_elem_flag_test(vert, BM_ELEM_HIDDEN))
1184                         {
1185                                 bool select = false;
1186                                 switch (type) {
1187                                         case SIMVERT_EDGE:
1188                                         {
1189                                                 const int num_edges = BM_vert_edge_count(vert);
1190                                                 GSetIterator gs_iter;
1191                                                 GSET_ITER(gs_iter, gset) {
1192                                                         const int num_edges_iter = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
1193                                                         const int delta_i = num_edges - num_edges_iter;
1194                                                         if (mesh_select_similar_compare_int(delta_i, compare)) {
1195                                                                 select = true;
1196                                                                 break;
1197                                                         }
1198                                                 }
1199                                                 break;
1200                                         }
1201                                         case SIMVERT_FACE:
1202                                         {
1203                                                 const int num_faces = BM_vert_face_count(vert);
1204                                                 GSetIterator gs_iter;
1205                                                 GSET_ITER(gs_iter, gset) {
1206                                                         const int num_faces_iter = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
1207                                                         const int delta_i = num_faces - num_faces_iter;
1208                                                         if (mesh_select_similar_compare_int(delta_i, compare)) {
1209                                                                 select = true;
1210                                                                 break;
1211                                                         }
1212                                                 }
1213                                                 break;
1214                                         }
1215                                         case SIMVERT_NORMAL:
1216                                         {
1217                                                 float normal[3];
1218                                                 copy_v3_v3(normal, vert->no);
1219                                                 mul_transposed_mat3_m4_v3(ob->imat, normal);
1220                                                 normalize_v3(normal);
1221
1222                                                 /* We are treating the normals as coordinates, the "nearest" one will
1223                                                  * also be the one closest to the angle. */
1224                                                 KDTreeNearest nearest;
1225                                                 if (BLI_kdtree_find_nearest(tree, normal, &nearest) != -1) {
1226                                                         if (angle_normalized_v3v3(normal, nearest.co) <= thresh_radians) {
1227                                                                 select = true;
1228                                                         }
1229                                                 }
1230                                                 break;
1231                                         }
1232                                         case SIMVERT_VGROUP:
1233                                         {
1234                                                 MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(vert, cd_dvert_offset);
1235                                                 MDeformWeight *dw = dvert->dw;
1236
1237                                                 for (int i = 0; i < dvert->totweight; i++, dw++) {
1238                                                         if (dw->weight > 0.0f) {
1239                                                                 if (dvert_selected & (1 << dw->def_nr)) {
1240                                                                         select = true;
1241                                                                         break;
1242                                                                 }
1243                                                         }
1244                                                 }
1245                                                 break;
1246                                         }
1247                                 }
1248
1249                                 if (select) {
1250                                         BM_vert_select_set(bm, vert, true);
1251                                         changed = true;
1252                                 }
1253                         }
1254                 }
1255
1256                 if (changed) {
1257                         EDBM_selectmode_flush(em);
1258                         EDBM_update_generic(em, false, false);
1259                 }
1260         }
1261
1262         MEM_freeN(objects);
1263         BLI_kdtree_free(tree);
1264         if (gset != NULL) {
1265                 BLI_gset_free(gset, NULL);
1266         }
1267
1268         return OPERATOR_FINISHED;
1269 }
1270 /** \} */
1271
1272 /* -------------------------------------------------------------------- */
1273 /** \name Select Similar Operator
1274  * \{ */
1275
1276 static int edbm_select_similar_exec(bContext *C, wmOperator *op)
1277 {
1278         ToolSettings *ts = CTX_data_tool_settings(C);
1279         PropertyRNA *prop = RNA_struct_find_property(op->ptr, "threshold");
1280
1281         const int type = RNA_enum_get(op->ptr, "type");
1282
1283         if (!RNA_property_is_set(op->ptr, prop)) {
1284                 RNA_property_float_set(op->ptr, prop, ts->select_thresh);
1285         }
1286         else {
1287                 ts->select_thresh = RNA_property_float_get(op->ptr, prop);
1288         }
1289
1290         if      (type < 100) return similar_vert_select_exec(C, op);
1291         else if (type < 200) return similar_edge_select_exec(C, op);
1292         else                 return similar_face_select_exec(C, op);
1293 }
1294
1295 static const EnumPropertyItem *select_similar_type_itemf(
1296         bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop),
1297         bool *r_free)
1298 {
1299         Object *obedit;
1300
1301         if (!C) /* needed for docs and i18n tools */
1302                 return prop_similar_types;
1303
1304         obedit = CTX_data_edit_object(C);
1305
1306         if (obedit && obedit->type == OB_MESH) {
1307                 EnumPropertyItem *item = NULL;
1308                 int a, totitem = 0;
1309                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
1310
1311                 if (em->selectmode & SCE_SELECT_VERTEX) {
1312                         for (a = SIMVERT_NORMAL; a < SIMEDGE_LENGTH; a++) {
1313                                 RNA_enum_items_add_value(&item, &totitem, prop_similar_types, a);
1314                         }
1315                 }
1316                 else if (em->selectmode & SCE_SELECT_EDGE) {
1317                         for (a = SIMEDGE_LENGTH; a < SIMFACE_MATERIAL; a++) {
1318                                 RNA_enum_items_add_value(&item, &totitem, prop_similar_types, a);
1319                         }
1320                 }
1321                 else if (em->selectmode & SCE_SELECT_FACE) {
1322 #ifdef WITH_FREESTYLE
1323                         const int a_end = SIMFACE_FREESTYLE;
1324 #else
1325                         const int a_end = SIMFACE_FACEMAP;
1326 #endif
1327                         for (a = SIMFACE_MATERIAL; a <= a_end; a++) {
1328                                 RNA_enum_items_add_value(&item, &totitem, prop_similar_types, a);
1329                         }
1330                 }
1331                 RNA_enum_item_end(&item, &totitem);
1332
1333                 *r_free = true;
1334
1335                 return item;
1336         }
1337
1338         return prop_similar_types;
1339 }
1340
1341 void MESH_OT_select_similar(wmOperatorType *ot)
1342 {
1343         PropertyRNA *prop;
1344
1345         /* identifiers */
1346         ot->name = "Select Similar";
1347         ot->idname = "MESH_OT_select_similar";
1348         ot->description = "Select similar vertices, edges or faces by property types";
1349
1350         /* api callbacks */
1351         ot->invoke = WM_menu_invoke;
1352         ot->exec = edbm_select_similar_exec;
1353         ot->poll = ED_operator_editmesh;
1354
1355         /* flags */
1356         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1357
1358         /* properties */
1359         prop = ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, SIMVERT_NORMAL, "Type", "");
1360         RNA_def_enum_funcs(prop, select_similar_type_itemf);
1361
1362         RNA_def_enum(ot->srna, "compare", prop_similar_compare_types, SIM_CMP_EQ, "Compare", "");
1363
1364         RNA_def_float(ot->srna, "threshold", 0.0f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f);
1365 }
1366
1367 /** \} */