Blender V5.0
editmesh_select_similar.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2004 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "MEM_guardedalloc.h"
10
11#include "BLI_bitmap.h"
12#include "BLI_kdtree.h"
13#include "BLI_listbase.h"
14#include "BLI_math_geom.h"
15#include "BLI_math_matrix.h"
16#include "BLI_math_vector.h"
17#include "BLI_set.hh"
18
19#include "BLT_translation.hh"
20
21#include "BKE_context.hh"
22#include "BKE_customdata.hh"
23#include "BKE_deform.hh"
24#include "BKE_editmesh.hh"
25#include "BKE_layer.hh"
26#include "BKE_material.hh"
27#include "BKE_object_types.hh"
28#include "BKE_report.hh"
29
30#include "DNA_meshdata_types.h"
31
32#include "WM_api.hh"
33#include "WM_types.hh"
34
35#include "RNA_access.hh"
36#include "RNA_define.hh"
37
38#include "ED_mesh.hh"
39#include "ED_screen.hh"
40#include "ED_select_utils.hh"
41
42#include "mesh_intern.hh" /* own include */
43
44using blender::Vector;
45
46/* -------------------------------------------------------------------- */
49
51 {SIM_CMP_EQ, "EQUAL", 0, "Equal", ""},
52 {SIM_CMP_GT, "GREATER", 0, "Greater", ""},
53 {SIM_CMP_LT, "LESS", 0, "Less", ""},
54 {0, nullptr, 0, nullptr, nullptr},
55};
56
58 {SIMVERT_NORMAL, "VERT_NORMAL", 0, "Normal", ""},
59 {SIMVERT_FACE, "VERT_FACES", 0, "Amount of Adjacent Faces", ""},
60 {SIMVERT_VGROUP, "VERT_GROUPS", 0, "Vertex Groups", ""},
61 {SIMVERT_EDGE, "VERT_EDGES", 0, "Amount of Connecting Edges", ""},
62 {SIMVERT_CREASE, "VERT_CREASE", 0, "Vertex Crease", ""},
63
64 {SIMEDGE_LENGTH, "EDGE_LENGTH", 0, "Length", ""},
65 {SIMEDGE_DIR, "EDGE_DIR", 0, "Direction", ""},
66 {SIMEDGE_FACE, "EDGE_FACES", 0, "Amount of Faces Around an Edge", ""},
67 {SIMEDGE_FACE_ANGLE, "EDGE_FACE_ANGLE", 0, "Face Angles", ""},
68 {SIMEDGE_CREASE, "EDGE_CREASE", 0, "Crease", ""},
69 {SIMEDGE_BEVEL, "EDGE_BEVEL", 0, "Bevel", ""},
70 {SIMEDGE_SEAM, "EDGE_SEAM", 0, "Seam", ""},
71 {SIMEDGE_SHARP, "EDGE_SHARP", 0, "Sharpness", ""},
72#ifdef WITH_FREESTYLE
73 {SIMEDGE_FREESTYLE, "EDGE_FREESTYLE", 0, "Freestyle Edge Marks", ""},
74#endif
75
76 {SIMFACE_MATERIAL, "FACE_MATERIAL", 0, "Material", ""},
77 {SIMFACE_AREA, "FACE_AREA", 0, "Area", ""},
78 {SIMFACE_SIDES, "FACE_SIDES", 0, "Polygon Sides", ""},
79 {SIMFACE_PERIMETER, "FACE_PERIMETER", 0, "Perimeter", ""},
80 {SIMFACE_NORMAL, "FACE_NORMAL", 0, "Normal", ""},
81 {SIMFACE_COPLANAR, "FACE_COPLANAR", 0, "Coplanar", ""},
82 {SIMFACE_SMOOTH, "FACE_SMOOTH", 0, "Flat/Smooth", ""},
83#ifdef WITH_FREESTYLE
84 {SIMFACE_FREESTYLE, "FACE_FREESTYLE", 0, "Freestyle Face Marks", ""},
85#endif
86
87 {0, nullptr, 0, nullptr, nullptr},
88};
89
90static int mesh_select_similar_compare_int(const int delta, const int compare)
91{
92 switch (compare) {
93 case SIM_CMP_EQ:
94 return (delta == 0);
95 case SIM_CMP_GT:
96 return (delta > 0);
97 case SIM_CMP_LT:
98 return (delta < 0);
99 default:
100 BLI_assert(0);
101 return 0;
102 }
103}
104
106
107/* -------------------------------------------------------------------- */
110
111enum {
116};
117
123static bool face_data_value_set(BMFace *face, const int hflag, int *r_value)
124{
125 if (BM_elem_flag_test(face, hflag)) {
126 *r_value |= SIMFACE_DATA_TRUE;
127 }
128 else {
129 *r_value |= SIMFACE_DATA_FALSE;
130 }
131
132 return *r_value != SIMFACE_DATA_ALL;
133}
134
138static void face_to_plane(const Object *ob, BMFace *face, float r_plane[4])
139{
140 float normal[3], co[3];
141 copy_v3_v3(normal, face->no);
142 mul_transposed_mat3_m4_v3(ob->world_to_object().ptr(), normal);
143 normalize_v3(normal);
144 mul_v3_m4v3(co, ob->object_to_world().ptr(), BM_FACE_FIRST_LOOP(face)->v->co);
145 plane_from_point_normal_v3(r_plane, co, normal);
146}
147
148/* TODO(dfelinto): `types` that should technically be compared in world space but are not:
149 * -SIMFACE_AREA
150 * -SIMFACE_PERIMETER
151 */
153{
154 const Scene *scene = CTX_data_scene(C);
155 ViewLayer *view_layer = CTX_data_view_layer(C);
156
157 const int type = RNA_enum_get(op->ptr, "type");
158 const float thresh = RNA_float_get(op->ptr, "threshold");
159 const float thresh_radians = thresh * float(M_PI);
160 const int compare = RNA_enum_get(op->ptr, "compare");
161
162 int tot_faces_selected_all = 0;
164 scene, view_layer, CTX_wm_view3d(C));
165
166 for (Object *ob : objects) {
168 tot_faces_selected_all += em->bm->totfacesel;
169 }
170
171 if (tot_faces_selected_all == 0) {
172 BKE_report(op->reports, RPT_ERROR, "No face selected");
173 return OPERATOR_CANCELLED;
174 }
175
176 KDTree_1d *tree_1d = nullptr;
177 KDTree_3d *tree_3d = nullptr;
178 KDTree_4d *tree_4d = nullptr;
179 GSet *gset = nullptr;
180 int face_data_value = SIMFACE_DATA_NONE;
181
182 switch (type) {
183 case SIMFACE_AREA:
185 tree_1d = BLI_kdtree_1d_new(tot_faces_selected_all);
186 break;
187 case SIMFACE_NORMAL:
188 tree_3d = BLI_kdtree_3d_new(tot_faces_selected_all);
189 break;
190 case SIMFACE_COPLANAR:
191 tree_4d = BLI_kdtree_4d_new(tot_faces_selected_all);
192 break;
193 case SIMFACE_SIDES:
194 case SIMFACE_MATERIAL:
195 gset = BLI_gset_ptr_new("Select similar face");
196 break;
197 }
198
199 int tree_index = 0;
200 for (Object *ob : objects) {
202 BMesh *bm = em->bm;
203 Material ***material_array = nullptr;
204 invert_m4_m4(ob->runtime->world_to_object.ptr(), ob->object_to_world().ptr());
205
206 if (bm->totfacesel == 0) {
207 continue;
208 }
209
210 float ob_m3[3][3];
211 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
212
213 int custom_data_offset = -1;
214 switch (type) {
215 case SIMFACE_MATERIAL: {
216 if (ob->totcol == 0) {
217 continue;
218 }
219 material_array = BKE_object_material_array_p(ob);
220 break;
221 }
222 case SIMFACE_FREESTYLE: {
223 custom_data_offset = CustomData_get_offset_named(
224 &bm->pdata, CD_PROP_BOOL, "freestyle_face");
225 if (custom_data_offset == -1) {
226 face_data_value |= SIMFACE_DATA_FALSE;
227 continue;
228 }
229 break;
230 }
231 }
232
233 BMFace *face; /* Mesh face. */
234 BMIter iter; /* Selected faces iterator. */
235
236 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
238 switch (type) {
239 case SIMFACE_SIDES:
240 BLI_gset_add(gset, POINTER_FROM_INT(face->len));
241 break;
242 case SIMFACE_MATERIAL: {
243 Material *material = (*material_array)[face->mat_nr];
244 if (material != nullptr) {
245 BLI_gset_add(gset, material);
246 }
247 break;
248 }
249 case SIMFACE_AREA: {
250 float area = BM_face_calc_area_with_mat3(face, ob_m3);
251 BLI_kdtree_1d_insert(tree_1d, tree_index++, &area);
252 break;
253 }
254 case SIMFACE_PERIMETER: {
255 float perimeter = BM_face_calc_perimeter_with_mat3(face, ob_m3);
256 BLI_kdtree_1d_insert(tree_1d, tree_index++, &perimeter);
257 break;
258 }
259 case SIMFACE_NORMAL: {
260 float normal[3];
261 copy_v3_v3(normal, face->no);
262 mul_transposed_mat3_m4_v3(ob->world_to_object().ptr(), normal);
263 normalize_v3(normal);
264 BLI_kdtree_3d_insert(tree_3d, tree_index++, normal);
265 break;
266 }
267 case SIMFACE_COPLANAR: {
268 float plane[4];
269 face_to_plane(ob, face, plane);
270 BLI_kdtree_4d_insert(tree_4d, tree_index++, plane);
271 break;
272 }
273 case SIMFACE_SMOOTH: {
274 if (!face_data_value_set(face, BM_ELEM_SMOOTH, &face_data_value)) {
275 goto face_select_all;
276 }
277 break;
278 }
279 case SIMFACE_FREESTYLE: {
280 if (custom_data_offset == -1 || !BM_ELEM_CD_GET_BOOL(face, custom_data_offset)) {
281 face_data_value |= SIMFACE_DATA_FALSE;
282 }
283 else {
284 face_data_value |= SIMFACE_DATA_TRUE;
285 }
286 if (face_data_value == SIMFACE_DATA_ALL) {
287 goto face_select_all;
288 }
289 break;
290 }
291 }
292 }
293 }
294 }
295
296 BLI_assert((type != SIMFACE_FREESTYLE) || (face_data_value != SIMFACE_DATA_NONE));
297
298 if (tree_1d != nullptr) {
299 BLI_kdtree_1d_deduplicate(tree_1d);
300 BLI_kdtree_1d_balance(tree_1d);
301 }
302 if (tree_3d != nullptr) {
303 BLI_kdtree_3d_deduplicate(tree_3d);
304 BLI_kdtree_3d_balance(tree_3d);
305 }
306 if (tree_4d != nullptr) {
307 BLI_kdtree_4d_deduplicate(tree_4d);
308 BLI_kdtree_4d_balance(tree_4d);
309 }
310
311 for (Object *ob : objects) {
313 BMesh *bm = em->bm;
314 bool changed = false;
315 Material ***material_array = nullptr;
316
317 float ob_m3[3][3];
318 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
319
320 int custom_data_offset = -1;
321 switch (type) {
322 case SIMFACE_MATERIAL: {
323 if (ob->totcol == 0) {
324 continue;
325 }
326 material_array = BKE_object_material_array_p(ob);
327 break;
328 }
329 case SIMFACE_FREESTYLE: {
330 custom_data_offset = CustomData_get_offset_named(
331 &bm->pdata, CD_PROP_BOOL, "freestyle_face");
332 if ((face_data_value == SIMFACE_DATA_TRUE) && (custom_data_offset == -1)) {
333 continue;
334 }
335 break;
336 }
337 }
338
339 BMFace *face; /* Mesh face. */
340 BMIter iter; /* Selected faces iterator. */
341
342 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
344 bool select = false;
345 switch (type) {
346 case SIMFACE_SIDES: {
347 const int num_sides = face->len;
348 GSetIterator gs_iter;
349 GSET_ITER (gs_iter, gset) {
350 const int num_sides_iter = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
351 const int delta_i = num_sides - num_sides_iter;
352 if (mesh_select_similar_compare_int(delta_i, compare)) {
353 select = true;
354 break;
355 }
356 }
357 break;
358 }
359 case SIMFACE_MATERIAL: {
360 const Material *material = (*material_array)[face->mat_nr];
361 if (material == nullptr) {
362 continue;
363 }
364
365 GSetIterator gs_iter;
366 GSET_ITER (gs_iter, gset) {
367 const Material *material_iter = static_cast<const Material *>(
368 BLI_gsetIterator_getKey(&gs_iter));
369 if (material == material_iter) {
370 select = true;
371 break;
372 }
373 }
374 break;
375 }
376 case SIMFACE_AREA: {
377 float area = BM_face_calc_area_with_mat3(face, ob_m3);
378 if (ED_select_similar_compare_float_tree(tree_1d, area, thresh, eSimilarCmp(compare)))
379 {
380 select = true;
381 }
382 break;
383 }
384 case SIMFACE_PERIMETER: {
385 float perimeter = BM_face_calc_perimeter_with_mat3(face, ob_m3);
387 tree_1d, perimeter, thresh, eSimilarCmp(compare)))
388 {
389 select = true;
390 }
391 break;
392 }
393 case SIMFACE_NORMAL: {
394 float normal[3];
395 copy_v3_v3(normal, face->no);
396 mul_transposed_mat3_m4_v3(ob->world_to_object().ptr(), normal);
397 normalize_v3(normal);
398
399 /* We are treating the normals as coordinates, the "nearest" one will
400 * also be the one closest to the angle. */
401 KDTreeNearest_3d nearest;
402 if (BLI_kdtree_3d_find_nearest(tree_3d, normal, &nearest) != -1) {
403 if (angle_normalized_v3v3(normal, nearest.co) <= thresh_radians) {
404 select = true;
405 }
406 }
407 break;
408 }
409 case SIMFACE_COPLANAR: {
410 float plane[4];
411 face_to_plane(ob, face, plane);
412
413 KDTreeNearest_4d nearest;
414 if (BLI_kdtree_4d_find_nearest(tree_4d, plane, &nearest) != -1) {
415 if (nearest.dist <= thresh) {
416 if ((fabsf(plane[3] - nearest.co[3]) <= thresh) &&
417 (angle_v3v3(plane, nearest.co) <= thresh_radians))
418 {
419 select = true;
420 }
421 }
422 }
423 break;
424 }
425 case SIMFACE_SMOOTH:
426 if ((BM_elem_flag_test(face, BM_ELEM_SMOOTH) != 0) ==
427 ((face_data_value & SIMFACE_DATA_TRUE) != 0))
428 {
429 select = true;
430 }
431 break;
432 case SIMFACE_FREESTYLE: {
433
434 if (custom_data_offset == -1) {
435 BLI_assert(face_data_value == SIMFACE_DATA_FALSE);
436 select = true;
437 break;
438 }
439
440 const bool value = BM_ELEM_CD_GET_BOOL(face, custom_data_offset);
441 if (value == ((face_data_value & SIMFACE_DATA_TRUE) != 0)) {
442 select = true;
443 }
444 break;
445 }
446 }
447
448 if (select) {
449 BM_face_select_set(bm, face, true);
450 changed = true;
451 }
452 }
453 }
454
455 if (changed) {
458
460 params.calc_looptris = false;
461 params.calc_normals = false;
462 params.is_destructive = false;
463 EDBM_update(static_cast<Mesh *>(ob->data), &params);
464 }
465 }
466
467 if (false) {
468 face_select_all:
470
471 for (Object *ob : objects) {
473 BMesh *bm = em->bm;
474
475 BMFace *face; /* Mesh face. */
476 BMIter iter; /* Selected faces iterator. */
477
478 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
479 if (!BM_elem_flag_test(face, BM_ELEM_SELECT)) {
480 BM_face_select_set(bm, face, true);
481 }
482 }
485
487 params.calc_looptris = false;
488 params.calc_normals = false;
489 params.is_destructive = false;
490 EDBM_update(static_cast<Mesh *>(ob->data), &params);
491 }
492 }
493
494 BLI_kdtree_1d_free(tree_1d);
495 BLI_kdtree_3d_free(tree_3d);
496 BLI_kdtree_4d_free(tree_4d);
497 if (gset != nullptr) {
498 BLI_gset_free(gset, nullptr);
499 }
500
501 return OPERATOR_FINISHED;
502}
503
505
506/* -------------------------------------------------------------------- */
509
510static void edge_pos_direction_worldspace_get(Object *ob, BMEdge *edge, float *r_dir)
511{
512 float v1[3], v2[3];
513 copy_v3_v3(v1, edge->v1->co);
514 copy_v3_v3(v2, edge->v2->co);
515
516 mul_m4_v3(ob->object_to_world().ptr(), v1);
517 mul_m4_v3(ob->object_to_world().ptr(), v2);
518
519 sub_v3_v3v3(r_dir, v1, v2);
520 normalize_v3(r_dir);
521}
522
524{
525 float v1[3], v2[3];
526
527 mul_v3_mat3_m4v3(v1, ob->object_to_world().ptr(), edge->v1->co);
528 mul_v3_mat3_m4v3(v2, ob->object_to_world().ptr(), edge->v2->co);
529
530 return len_squared_v3v3(v1, v2);
531}
532
533enum {
538};
539
545static bool edge_data_value_set(BMEdge *edge, const int hflag, int *r_value)
546{
547 if (BM_elem_flag_test(edge, hflag)) {
548 *r_value |= SIMEDGE_DATA_TRUE;
549 }
550 else {
551 *r_value |= SIMEDGE_DATA_FALSE;
552 }
553
554 return *r_value != SIMEDGE_DATA_ALL;
555}
556
557/* TODO(dfelinto): `types` that should technically be compared in world space but are not:
558 * -SIMEDGE_FACE_ANGLE
559 */
561{
562 const Scene *scene = CTX_data_scene(C);
563 ViewLayer *view_layer = CTX_data_view_layer(C);
564
565 const int type = RNA_enum_get(op->ptr, "type");
566 const float thresh = RNA_float_get(op->ptr, "threshold");
567 const float thresh_radians = thresh * float(M_PI) + FLT_EPSILON;
568 const int compare = RNA_enum_get(op->ptr, "compare");
569
570 int tot_edges_selected_all = 0;
572 scene, view_layer, CTX_wm_view3d(C));
573
574 for (Object *ob : objects) {
576 tot_edges_selected_all += em->bm->totedgesel;
577 }
578
579 if (tot_edges_selected_all == 0) {
580 BKE_report(op->reports, RPT_ERROR, "No edge selected");
581 return OPERATOR_CANCELLED;
582 }
583
584 KDTree_1d *tree_1d = nullptr;
585 KDTree_3d *tree_3d = nullptr;
586 GSet *gset = nullptr;
587 int edge_data_value = SIMEDGE_DATA_NONE;
588
589 switch (type) {
590 case SIMEDGE_CREASE:
591 case SIMEDGE_BEVEL:
593 case SIMEDGE_LENGTH:
594 tree_1d = BLI_kdtree_1d_new(tot_edges_selected_all);
595 break;
596 case SIMEDGE_DIR:
597 tree_3d = BLI_kdtree_3d_new(tot_edges_selected_all * 2);
598 break;
599 case SIMEDGE_FACE:
600 gset = BLI_gset_ptr_new("Select similar edge: face");
601 break;
602 }
603
604 int tree_index = 0;
605 for (Object *ob : objects) {
607 BMesh *bm = em->bm;
608
609 if (bm->totedgesel == 0) {
610 continue;
611 }
612
613 switch (type) {
614 case SIMEDGE_FREESTYLE: {
615 if (!CustomData_has_layer_named(&bm->edata, CD_PROP_BOOL, "freestyle_edge")) {
616 edge_data_value |= SIMEDGE_DATA_FALSE;
617 continue;
618 }
619 break;
620 }
621 case SIMEDGE_CREASE: {
622 if (!CustomData_has_layer_named(&bm->edata, CD_PROP_FLOAT, "crease_edge")) {
623 float pos = 0.0f;
624 BLI_kdtree_1d_insert(tree_1d, tree_index++, &pos);
625 continue;
626 }
627 break;
628 }
629 case SIMEDGE_BEVEL: {
630 if (!CustomData_has_layer_named(&bm->edata, CD_PROP_FLOAT, "bevel_weight_edge")) {
631 float pos = 0.0f;
632 BLI_kdtree_1d_insert(tree_1d, tree_index++, &pos);
633 continue;
634 }
635 break;
636 }
637 }
638
639 int custom_data_offset;
640 switch (type) {
641 case SIMEDGE_FREESTYLE: {
642 custom_data_offset = CustomData_get_offset_named(
643 &bm->edata, CD_PROP_BOOL, "freestyle_edge");
644 break;
645 }
646 case SIMEDGE_CREASE:
647 custom_data_offset = CustomData_get_offset_named(&bm->edata, CD_PROP_FLOAT, "crease_edge");
648 break;
649 case SIMEDGE_BEVEL:
650 custom_data_offset = CustomData_get_offset_named(
651 &bm->edata, CD_PROP_FLOAT, "bevel_weight_edge");
652 break;
653 }
654
655 float ob_m3[3][3], ob_m3_inv[3][3];
656 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
657 invert_m3_m3(ob_m3_inv, ob_m3);
658
659 BMEdge *edge; /* Mesh edge. */
660 BMIter iter; /* Selected edges iterator. */
661
662 BM_ITER_MESH (edge, &iter, bm, BM_EDGES_OF_MESH) {
664 switch (type) {
665 case SIMEDGE_FACE:
667 break;
668 case SIMEDGE_DIR: {
669 float dir[3], dir_flip[3];
671 BLI_kdtree_3d_insert(tree_3d, tree_index++, dir);
672 /* Also store the flipped direction so it can be checked regardless of the verts order
673 * of the edges. */
674 negate_v3_v3(dir_flip, dir);
675 BLI_kdtree_3d_insert(tree_3d, tree_index++, dir_flip);
676 break;
677 }
678 case SIMEDGE_LENGTH: {
680 BLI_kdtree_1d_insert(tree_1d, tree_index++, &length);
681 break;
682 }
683 case SIMEDGE_FACE_ANGLE: {
684 if (BM_edge_face_count_at_most(edge, 2) == 2) {
685 float angle = BM_edge_calc_face_angle_with_imat3(edge, ob_m3_inv);
686 BLI_kdtree_1d_insert(tree_1d, tree_index++, &angle);
687 }
688 break;
689 }
690 case SIMEDGE_SEAM:
691 if (!edge_data_value_set(edge, BM_ELEM_SEAM, &edge_data_value)) {
692 goto edge_select_all;
693 }
694 break;
695 case SIMEDGE_SHARP:
696 if (!edge_data_value_set(edge, BM_ELEM_SMOOTH, &edge_data_value)) {
697 goto edge_select_all;
698 }
699 break;
700 case SIMEDGE_FREESTYLE: {
701 if (custom_data_offset == -1 || !BM_ELEM_CD_GET_BOOL(edge, custom_data_offset)) {
702 edge_data_value |= SIMEDGE_DATA_FALSE;
703 }
704 else {
705 edge_data_value |= SIMEDGE_DATA_TRUE;
706 }
707 if (edge_data_value == SIMEDGE_DATA_ALL) {
708 goto edge_select_all;
709 }
710 break;
711 }
712 case SIMEDGE_CREASE:
713 case SIMEDGE_BEVEL: {
714 const float *value = BM_ELEM_CD_GET_FLOAT_P(edge, custom_data_offset);
715 BLI_kdtree_1d_insert(tree_1d, tree_index++, value);
716 break;
717 }
718 }
719 }
720 }
721 }
722
723 BLI_assert((type != SIMEDGE_FREESTYLE) || (edge_data_value != SIMEDGE_DATA_NONE));
724
725 if (tree_1d != nullptr) {
726 BLI_kdtree_1d_deduplicate(tree_1d);
727 BLI_kdtree_1d_balance(tree_1d);
728 }
729 if (tree_3d != nullptr) {
730 BLI_kdtree_3d_deduplicate(tree_3d);
731 BLI_kdtree_3d_balance(tree_3d);
732 }
733
734 for (Object *ob : objects) {
736 BMesh *bm = em->bm;
737 bool changed = false;
738
739 bool has_custom_data_layer = false;
740 switch (type) {
741 case SIMEDGE_FREESTYLE: {
742 has_custom_data_layer = CustomData_has_layer_named(
743 &bm->edata, CD_PROP_BOOL, "freestyle_edge");
744 if ((edge_data_value == SIMEDGE_DATA_TRUE) && !has_custom_data_layer) {
745 continue;
746 }
747 break;
748 }
749 case SIMEDGE_CREASE: {
750 has_custom_data_layer = CustomData_has_layer_named(
751 &bm->edata, CD_PROP_FLOAT, "crease_edge");
752 if (!has_custom_data_layer) {
753 /* Proceed only if we have to select all the edges that have custom data value of 0.0f.
754 * In this case we will just select all the edges.
755 * Otherwise continue the for loop. */
756 if (!ED_select_similar_compare_float_tree(tree_1d, 0.0f, thresh, eSimilarCmp(compare))) {
757 continue;
758 }
759 }
760 break;
761 }
762 case SIMEDGE_BEVEL: {
763 has_custom_data_layer = CustomData_has_layer_named(
764 &bm->edata, CD_PROP_FLOAT, "bevel_weight_edge");
765 if (!has_custom_data_layer) {
766 /* Proceed only if we have to select all the edges that have custom data value of 0.0f.
767 * In this case we will just select all the edges.
768 * Otherwise continue the for loop. */
769 if (!ED_select_similar_compare_float_tree(tree_1d, 0.0f, thresh, eSimilarCmp(compare))) {
770 continue;
771 }
772 }
773 break;
774 }
775 }
776
777 float ob_m3[3][3], ob_m3_inv[3][3];
778 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
779 invert_m3_m3(ob_m3_inv, ob_m3);
780
781 int custom_data_offset;
782 switch (type) {
784 custom_data_offset = CustomData_get_offset_named(
785 &bm->edata, CD_PROP_BOOL, "freestyle_edge");
786 break;
787 case SIMEDGE_CREASE:
788 custom_data_offset = CustomData_get_offset_named(&bm->edata, CD_PROP_FLOAT, "crease_edge");
789 break;
790 case SIMEDGE_BEVEL:
791 custom_data_offset = CustomData_get_offset_named(
792 &bm->edata, CD_PROP_FLOAT, "bevel_weight_edge");
793 break;
794 }
795
796 BMEdge *edge; /* Mesh edge. */
797 BMIter iter; /* Selected edges iterator. */
798
799 BM_ITER_MESH (edge, &iter, bm, BM_EDGES_OF_MESH) {
801 bool select = false;
802 switch (type) {
803 case SIMEDGE_FACE: {
804 const int num_faces = BM_edge_face_count(edge);
805 GSetIterator gs_iter;
806 GSET_ITER (gs_iter, gset) {
807 const int num_faces_iter = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
808 const int delta_i = num_faces - num_faces_iter;
809 if (mesh_select_similar_compare_int(delta_i, compare)) {
810 select = true;
811 break;
812 }
813 }
814 break;
815 }
816 case SIMEDGE_DIR: {
817 float dir[3];
819
820 /* We are treating the direction as coordinates, the "nearest" one will
821 * also be the one closest to the intended direction. */
822 KDTreeNearest_3d nearest;
823 if (BLI_kdtree_3d_find_nearest(tree_3d, dir, &nearest) != -1) {
824 if (angle_normalized_v3v3(dir, nearest.co) <= thresh_radians) {
825 select = true;
826 }
827 }
828 break;
829 }
830 case SIMEDGE_LENGTH: {
833 tree_1d, length, thresh, eSimilarCmp(compare)))
834 {
835 select = true;
836 }
837 break;
838 }
839 case SIMEDGE_FACE_ANGLE: {
840 if (BM_edge_face_count_at_most(edge, 2) == 2) {
841 float angle = BM_edge_calc_face_angle_with_imat3(edge, ob_m3_inv);
843 select = true;
844 }
845 }
846 break;
847 }
848 case SIMEDGE_SEAM:
849 if ((BM_elem_flag_test(edge, BM_ELEM_SEAM) != 0) ==
850 ((edge_data_value & SIMEDGE_DATA_TRUE) != 0))
851 {
852 select = true;
853 }
854 break;
855 case SIMEDGE_SHARP:
856 if ((BM_elem_flag_test(edge, BM_ELEM_SMOOTH) != 0) ==
857 ((edge_data_value & SIMEDGE_DATA_TRUE) != 0))
858 {
859 select = true;
860 }
861 break;
862 case SIMEDGE_FREESTYLE: {
863 if (!has_custom_data_layer) {
864 BLI_assert(edge_data_value == SIMEDGE_DATA_FALSE);
865 select = true;
866 break;
867 }
868
869 const bool value = BM_ELEM_CD_GET_BOOL(edge, custom_data_offset);
870 if (value == ((edge_data_value & SIMEDGE_DATA_TRUE) != 0)) {
871 select = true;
872 }
873 break;
874 }
875 case SIMEDGE_CREASE:
876 case SIMEDGE_BEVEL: {
877 if (!has_custom_data_layer) {
878 select = true;
879 break;
880 }
881
882 const float *value = BM_ELEM_CD_GET_FLOAT_P(edge, custom_data_offset);
884 tree_1d, *value, thresh, eSimilarCmp(compare)))
885 {
886 select = true;
887 }
888 break;
889 }
890 }
891
892 if (select) {
893 BM_edge_select_set(bm, edge, true);
894 changed = true;
895 }
896 }
897 }
898
899 if (changed) {
902
904 params.calc_looptris = false;
905 params.calc_normals = false;
906 params.is_destructive = false;
907 EDBM_update(static_cast<Mesh *>(ob->data), &params);
908 }
909 }
910
911 if (false) {
912 edge_select_all:
914
915 for (Object *ob : objects) {
917 BMesh *bm = em->bm;
918
919 BMEdge *edge; /* Mesh edge. */
920 BMIter iter; /* Selected edges iterator. */
921
922 BM_ITER_MESH (edge, &iter, bm, BM_EDGES_OF_MESH) {
923 if (!BM_elem_flag_test(edge, BM_ELEM_SELECT)) {
924 BM_edge_select_set(bm, edge, true);
925 }
926 }
929
931 params.calc_looptris = false;
932 params.calc_normals = false;
933 params.is_destructive = false;
934 EDBM_update(static_cast<Mesh *>(ob->data), &params);
935 }
936 }
937
938 BLI_kdtree_1d_free(tree_1d);
939 BLI_kdtree_3d_free(tree_3d);
940 if (gset != nullptr) {
941 BLI_gset_free(gset, nullptr);
942 }
943
944 return OPERATOR_FINISHED;
945}
946
948
949/* -------------------------------------------------------------------- */
952
954{
955 const Scene *scene = CTX_data_scene(C);
956 ViewLayer *view_layer = CTX_data_view_layer(C);
957
958 /* get the type from RNA */
959 const int type = RNA_enum_get(op->ptr, "type");
960 const float thresh = RNA_float_get(op->ptr, "threshold");
961 const float thresh_radians = thresh * float(M_PI) + FLT_EPSILON;
962 const int compare = RNA_enum_get(op->ptr, "compare");
963
964 int tot_verts_selected_all = 0;
966 scene, view_layer, CTX_wm_view3d(C));
967
968 for (Object *ob : objects) {
970 tot_verts_selected_all += em->bm->totvertsel;
971 }
972
973 if (tot_verts_selected_all == 0) {
974 BKE_report(op->reports, RPT_ERROR, "No vertex selected");
975 return OPERATOR_CANCELLED;
976 }
977
978 KDTree_3d *tree_3d = nullptr;
979 KDTree_1d *tree_1d = nullptr;
980 blender::Set<blender::StringRef> selected_vertex_groups;
981 blender::Set<int> connected_elems_num_set;
982
983 switch (type) {
984 case SIMVERT_NORMAL:
985 tree_3d = BLI_kdtree_3d_new(tot_verts_selected_all);
986 break;
987 case SIMVERT_CREASE:
988 tree_1d = BLI_kdtree_1d_new(tot_verts_selected_all);
989 break;
990 case SIMVERT_EDGE:
991 case SIMVERT_FACE:
992 case SIMVERT_VGROUP:
993 break;
994 }
995
996 int normal_tree_index = 0;
997 int tree_1d_index = 0;
998 for (Object *ob : objects) {
1000 BMesh *bm = em->bm;
1001 int cd_dvert_offset = -1;
1002 int cd_crease_offset = -1;
1003 BLI_bitmap *defbase_selected = nullptr;
1004 int defbase_len = 0;
1005
1006 invert_m4_m4(ob->runtime->world_to_object.ptr(), ob->object_to_world().ptr());
1007
1008 if (bm->totvertsel == 0) {
1009 continue;
1010 }
1011
1012 if (type == SIMVERT_VGROUP) {
1013 cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT);
1014 if (cd_dvert_offset == -1) {
1015 continue;
1016 }
1017 defbase_len = BKE_object_defgroup_count(ob);
1018 if (defbase_len == 0) {
1019 continue;
1020 }
1021 defbase_selected = BLI_BITMAP_NEW(defbase_len, __func__);
1022 }
1023 else if (type == SIMVERT_CREASE) {
1024 if (!CustomData_has_layer_named(&bm->vdata, CD_PROP_FLOAT, "crease_vert")) {
1025 float pos = 0.0f;
1026 BLI_kdtree_1d_insert(tree_1d, tree_1d_index++, &pos);
1027 continue;
1028 }
1029 cd_crease_offset = CustomData_get_offset_named(&bm->vdata, CD_PROP_FLOAT, "crease_vert");
1030 }
1031
1032 BMVert *vert; /* Mesh vertex. */
1033 BMIter iter; /* Selected verts iterator. */
1034
1035 BM_ITER_MESH (vert, &iter, bm, BM_VERTS_OF_MESH) {
1036 if (BM_elem_flag_test(vert, BM_ELEM_SELECT)) {
1037 switch (type) {
1038 case SIMVERT_FACE:
1039 connected_elems_num_set.add(BM_vert_face_count(vert));
1040 break;
1041 case SIMVERT_EDGE:
1042 connected_elems_num_set.add(BM_vert_edge_count(vert));
1043 break;
1044 case SIMVERT_NORMAL: {
1045 float normal[3];
1046 copy_v3_v3(normal, vert->no);
1047 mul_transposed_mat3_m4_v3(ob->world_to_object().ptr(), normal);
1048 normalize_v3(normal);
1049
1050 BLI_kdtree_3d_insert(tree_3d, normal_tree_index++, normal);
1051 break;
1052 }
1053 case SIMVERT_VGROUP: {
1054 MDeformVert *dvert = static_cast<MDeformVert *>(
1055 BM_ELEM_CD_GET_VOID_P(vert, cd_dvert_offset));
1056 MDeformWeight *dw = dvert->dw;
1057
1058 for (int i = 0; i < dvert->totweight; i++, dw++) {
1059 if (dw->weight > 0.0f) {
1060 if (LIKELY(dw->def_nr < defbase_len)) {
1061 BLI_BITMAP_ENABLE(defbase_selected, dw->def_nr);
1062 }
1063 }
1064 }
1065 break;
1066 }
1067 case SIMVERT_CREASE: {
1068 const float *value = BM_ELEM_CD_GET_FLOAT_P(vert, cd_crease_offset);
1069 BLI_kdtree_1d_insert(tree_1d, tree_1d_index++, value);
1070 break;
1071 }
1072 }
1073 }
1074 }
1075
1076 if (type == SIMVERT_VGROUP) {
1077 /* We store the names of the vertex groups, so we can select
1078 * vertex groups with the same name in different objects. */
1079
1080 const ListBase *defbase = BKE_object_defgroup_list(ob);
1081
1082 int i = 0;
1083 LISTBASE_FOREACH (bDeformGroup *, dg, defbase) {
1084 if (BLI_BITMAP_TEST(defbase_selected, i)) {
1085 selected_vertex_groups.add_as(dg->name);
1086 }
1087 i += 1;
1088 }
1089 MEM_freeN(defbase_selected);
1090 }
1091 }
1092
1093 if (type == SIMVERT_VGROUP) {
1094 if (selected_vertex_groups.is_empty()) {
1095 BKE_report(op->reports, RPT_INFO, "No vertex group among the selected vertices");
1096 }
1097 }
1098
1099 /* Remove duplicated entries. */
1100 if (tree_1d != nullptr) {
1101 BLI_kdtree_1d_deduplicate(tree_1d);
1102 BLI_kdtree_1d_balance(tree_1d);
1103 }
1104 if (tree_3d != nullptr) {
1105 BLI_kdtree_3d_deduplicate(tree_3d);
1106 BLI_kdtree_3d_balance(tree_3d);
1107 }
1108
1109 /* Run the matching operations. */
1110 for (Object *ob : objects) {
1112 BMesh *bm = em->bm;
1113 bool changed = false;
1114 bool has_crease_layer = false;
1115 int cd_dvert_offset = -1;
1116 int cd_crease_offset = -1;
1117 BLI_bitmap *defbase_selected = nullptr;
1118 int defbase_len = 0;
1119
1120 if (type == SIMVERT_VGROUP) {
1121 cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT);
1122 if (cd_dvert_offset == -1) {
1123 continue;
1124 }
1125 const ListBase *defbase = BKE_object_defgroup_list(ob);
1126 defbase_len = BLI_listbase_count(defbase);
1127 if (defbase_len == 0) {
1128 continue;
1129 }
1130
1131 /* We map back the names of the vertex groups to their corresponding indices
1132 * for this object. This is fast, and keep the logic for each vertex very simple. */
1133
1134 defbase_selected = BLI_BITMAP_NEW(defbase_len, __func__);
1135 bool found_any = false;
1136 for (const blender::StringRef name : selected_vertex_groups) {
1137 int vgroup_id = BKE_defgroup_name_index(defbase, name);
1138 if (vgroup_id != -1) {
1139 BLI_BITMAP_ENABLE(defbase_selected, vgroup_id);
1140 found_any = true;
1141 }
1142 }
1143 if (found_any == false) {
1144 MEM_freeN(defbase_selected);
1145 continue;
1146 }
1147 }
1148 else if (type == SIMVERT_CREASE) {
1149 cd_crease_offset = CustomData_get_offset_named(&bm->vdata, CD_PROP_FLOAT, "crease_vert");
1150 has_crease_layer = CustomData_has_layer_named(&bm->vdata, CD_PROP_FLOAT, "crease_vert");
1151 if (!has_crease_layer) {
1152 /* Proceed only if we have to select all the vertices that have custom data value of 0.0f.
1153 * In this case we will just select all the vertices.
1154 * Otherwise continue the for loop. */
1155 if (!ED_select_similar_compare_float_tree(tree_1d, 0.0f, thresh, eSimilarCmp(compare))) {
1156 continue;
1157 }
1158 }
1159 }
1160
1161 BMVert *vert; /* Mesh vertex. */
1162 BMIter iter; /* Selected verts iterator. */
1163
1164 BM_ITER_MESH (vert, &iter, bm, BM_VERTS_OF_MESH) {
1166 bool select = false;
1167 switch (type) {
1168 case SIMVERT_EDGE: {
1169 const int num_edges = BM_vert_edge_count(vert);
1170 for (const int num_edges_iter : connected_elems_num_set) {
1171 const int delta_i = num_edges - num_edges_iter;
1172 if (mesh_select_similar_compare_int(delta_i, compare)) {
1173 select = true;
1174 break;
1175 }
1176 }
1177 break;
1178 }
1179 case SIMVERT_FACE: {
1180 const int num_faces = BM_vert_face_count(vert);
1181 for (const int num_faces_iter : connected_elems_num_set) {
1182 const int delta_i = num_faces - num_faces_iter;
1183 if (mesh_select_similar_compare_int(delta_i, compare)) {
1184 select = true;
1185 break;
1186 }
1187 }
1188 break;
1189 }
1190 case SIMVERT_NORMAL: {
1191 float normal[3];
1192 copy_v3_v3(normal, vert->no);
1193 mul_transposed_mat3_m4_v3(ob->world_to_object().ptr(), normal);
1194 normalize_v3(normal);
1195
1196 /* We are treating the normals as coordinates, the "nearest" one will
1197 * also be the one closest to the angle. */
1198 KDTreeNearest_3d nearest;
1199 if (BLI_kdtree_3d_find_nearest(tree_3d, normal, &nearest) != -1) {
1200 if (angle_normalized_v3v3(normal, nearest.co) <= thresh_radians) {
1201 select = true;
1202 }
1203 }
1204 break;
1205 }
1206 case SIMVERT_VGROUP: {
1207 MDeformVert *dvert = static_cast<MDeformVert *>(
1208 BM_ELEM_CD_GET_VOID_P(vert, cd_dvert_offset));
1209 MDeformWeight *dw = dvert->dw;
1210
1211 for (int i = 0; i < dvert->totweight; i++, dw++) {
1212 if (dw->weight > 0.0f) {
1213 if (LIKELY(dw->def_nr < defbase_len)) {
1214 if (BLI_BITMAP_TEST(defbase_selected, dw->def_nr)) {
1215 select = true;
1216 break;
1217 }
1218 }
1219 }
1220 }
1221 break;
1222 }
1223 case SIMVERT_CREASE: {
1224 if (!has_crease_layer) {
1225 select = true;
1226 break;
1227 }
1228 const float *value = BM_ELEM_CD_GET_FLOAT_P(vert, cd_crease_offset);
1230 tree_1d, *value, thresh, eSimilarCmp(compare)))
1231 {
1232 select = true;
1233 }
1234 break;
1235 }
1236 }
1237
1238 if (select) {
1239 BM_vert_select_set(bm, vert, true);
1240 changed = true;
1241 }
1242 }
1243 }
1244
1245 if (type == SIMVERT_VGROUP) {
1246 MEM_freeN(defbase_selected);
1247 }
1248
1249 if (changed) {
1252 params.calc_looptris = false;
1253 params.calc_normals = false;
1254 params.is_destructive = false;
1255 EDBM_update(static_cast<Mesh *>(ob->data), &params);
1256 }
1257 }
1258
1259 BLI_kdtree_1d_free(tree_1d);
1260 BLI_kdtree_3d_free(tree_3d);
1261
1262 return OPERATOR_FINISHED;
1263}
1264
1266
1267/* -------------------------------------------------------------------- */
1270
1272{
1274 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "threshold");
1275
1276 const int type = RNA_enum_get(op->ptr, "type");
1277
1278 if (!RNA_property_is_set(op->ptr, prop)) {
1280 }
1281 else {
1282 ts->select_thresh = RNA_property_float_get(op->ptr, prop);
1283 }
1284
1285 if (type < 100) {
1286 return similar_vert_select_exec(C, op);
1287 }
1288 if (type < 200) {
1289 return similar_edge_select_exec(C, op);
1290 }
1291 return similar_face_select_exec(C, op);
1292}
1293
1295 PointerRNA * /*ptr*/,
1296 PropertyRNA * /*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
1305 obedit = CTX_data_edit_object(C);
1306
1307 if (obedit && obedit->type == OB_MESH) {
1308 EnumPropertyItem *item = nullptr;
1309 int a, totitem = 0;
1311
1312 if (em->selectmode & SCE_SELECT_VERTEX) {
1313 for (a = SIMVERT_NORMAL; a < SIMEDGE_LENGTH; a++) {
1314 RNA_enum_items_add_value(&item, &totitem, prop_similar_types, a);
1315 }
1316 }
1317 else if (em->selectmode & SCE_SELECT_EDGE) {
1318 for (a = SIMEDGE_LENGTH; a < SIMFACE_MATERIAL; a++) {
1319 RNA_enum_items_add_value(&item, &totitem, prop_similar_types, a);
1320 }
1321 }
1322 else if (em->selectmode & SCE_SELECT_FACE) {
1323#ifdef WITH_FREESTYLE
1324 const int a_end = SIMFACE_FREESTYLE;
1325#else
1326 const int a_end = SIMFACE_SMOOTH;
1327#endif
1328 for (a = SIMFACE_MATERIAL; a <= a_end; a++) {
1329 RNA_enum_items_add_value(&item, &totitem, prop_similar_types, a);
1330 }
1331 }
1332 RNA_enum_item_end(&item, &totitem);
1333
1334 *r_free = true;
1335
1336 return item;
1337 }
1338
1339 return prop_similar_types;
1340}
1341
1343 wmOperator *op,
1344 const PropertyRNA *prop)
1345{
1346 const char *prop_id = RNA_property_identifier(prop);
1347 const int type = RNA_enum_get(op->ptr, "type");
1348
1349 /* Only show compare when it is used. */
1350 if (STREQ(prop_id, "compare")) {
1351 if (type == SIMVERT_VGROUP) {
1352 return false;
1353 }
1354 }
1355 /* Only show threshold when it is used. */
1356 else if (STREQ(prop_id, "threshold")) {
1357 if (!ELEM(type,
1368 {
1369 return false;
1370 }
1371 }
1372
1373 return true;
1374}
1375
1377{
1378 PropertyRNA *prop;
1379
1380 /* identifiers */
1381 ot->name = "Select Similar";
1382 ot->idname = "MESH_OT_select_similar";
1383 ot->description = "Select similar vertices, edges or faces by property types";
1384
1385 /* API callbacks. */
1386 ot->invoke = WM_menu_invoke;
1388 ot->poll = ED_operator_editmesh;
1389 ot->poll_property = edbm_select_similar_poll_property;
1390
1391 /* flags */
1392 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1393
1394 /* properties */
1395 prop = ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, SIMVERT_NORMAL, "Type", "");
1398
1399 RNA_def_enum(ot->srna, "compare", prop_similar_compare_types, SIM_CMP_EQ, "Compare", "");
1400
1401 prop = RNA_def_float(ot->srna, "threshold", 0.0f, 0.0f, 100000.0f, "Threshold", "", 0.0f, 1.0f);
1402 /* Very small values are needed sometimes, similar area of small faces for e.g: see #87823 */
1403 RNA_def_property_ui_range(prop, 0.0, 1.0, 0.01, 5);
1404}
1405
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
ToolSettings * CTX_data_tool_settings(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
CustomData interface, see also DNA_customdata_types.h.
int CustomData_get_offset(const CustomData *data, eCustomDataType type)
bool CustomData_has_layer_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
int CustomData_get_offset_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
support for deformation groups and hooks.
int BKE_object_defgroup_count(const Object *ob)
Definition deform.cc:602
int BKE_defgroup_name_index(const ListBase *defbase, blender::StringRef name)
Definition deform.cc:540
const ListBase * BKE_object_defgroup_list(const Object *ob)
Definition deform.cc:585
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:61
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
General operations, lookup, etc. for materials.
Material *** BKE_object_material_array_p(Object *ob)
@ RPT_INFO
Definition BKE_report.hh:35
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_BITMAP_NEW(_num, _alloc_string)
Definition BLI_bitmap.h:37
#define BLI_BITMAP_TEST(_bitmap, _index)
Definition BLI_bitmap.h:61
#define BLI_BITMAP_ENABLE(_bitmap, _index)
Definition BLI_bitmap.h:78
unsigned int BLI_bitmap
Definition BLI_bitmap.h:13
struct GSet GSet
Definition BLI_ghash.h:337
GSet * BLI_gset_ptr_new(const char *info)
BLI_INLINE void * BLI_gsetIterator_getKey(GSetIterator *gsi)
Definition BLI_ghash.h:455
#define GSET_ITER(gs_iter_, gset_)
Definition BLI_ghash.h:468
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
bool BLI_gset_add(GSet *gs, void *key)
Definition BLI_ghash.cc:966
A KD-tree for nearest neighbor search.
#define LISTBASE_FOREACH(type, var, list)
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
#define M_PI
void plane_from_point_normal_v3(float r_plane[4], const float plane_co[3], const float plane_no[3])
Definition math_geom.cc:217
void copy_m3_m4(float m1[3][3], const float m2[4][4])
bool invert_m3_m3(float inverse[3][3], const float mat[3][3])
void mul_m4_v3(const float M[4][4], float r[3])
void mul_transposed_mat3_m4_v3(const float M[4][4], float r[3])
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
void mul_v3_mat3_m4v3(float r[3], const float mat[4][4], const float vec[3])
float angle_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void negate_v3_v3(float r[3], const float a[3])
float angle_normalized_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v3(float n[3])
#define POINTER_FROM_INT(i)
#define POINTER_AS_INT(i)
#define ELEM(...)
#define STREQ(a, b)
#define LIKELY(x)
#define BLT_I18NCONTEXT_ID_MESH
@ CD_PROP_FLOAT
@ CD_MDEFORMVERT
@ OB_MESH
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
void EDBM_update(Mesh *mesh, const EDBMUpdate_Params *params)
bool EDBM_uvselect_clear(BMEditMesh *em)
void EDBM_selectmode_flush(BMEditMesh *em)
bool ED_operator_editmesh(bContext *C)
eSimilarCmp
@ SIM_CMP_LT
@ SIM_CMP_GT
@ SIM_CMP_EQ
bool ED_select_similar_compare_float_tree(const KDTree_1d *tree, float length, float thresh, eSimilarCmp compare)
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
static const EnumPropertyItem prop_similar_types[]
#define BM_ELEM_CD_GET_BOOL(ele, offset)
#define BM_FACE_FIRST_LOOP(p)
#define BM_ELEM_CD_GET_FLOAT_P(ele, offset)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SEAM
@ BM_ELEM_SELECT
@ BM_ELEM_SMOOTH
#define BM_ELEM_CD_GET_VOID_P(ele, offset)
#define BM_elem_flag_test(ele, hflag)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
BMesh * bm
void BM_face_select_set(BMesh *bm, BMFace *f, const bool select)
Select Face.
void BM_vert_select_set(BMesh *bm, BMVert *v, const bool select)
Select Vert.
void BM_edge_select_set(BMesh *bm, BMEdge *e, const bool select)
Select Edge.
@ SIMEDGE_FACE_ANGLE
@ SIMEDGE_FACE
@ SIMEDGE_SHARP
@ SIMEDGE_DIR
@ SIMEDGE_BEVEL
@ SIMEDGE_FREESTYLE
@ SIMEDGE_CREASE
@ SIMEDGE_LENGTH
@ SIMEDGE_SEAM
@ SIMFACE_MATERIAL
@ SIMFACE_FREESTYLE
@ SIMFACE_AREA
@ SIMFACE_PERIMETER
@ SIMFACE_NORMAL
@ SIMFACE_SMOOTH
@ SIMFACE_COPLANAR
@ SIMFACE_SIDES
@ SIMVERT_VGROUP
@ SIMVERT_FACE
@ SIMVERT_EDGE
@ SIMVERT_NORMAL
@ SIMVERT_CREASE
float BM_face_calc_area_with_mat3(const BMFace *f, const float mat3[3][3])
float BM_face_calc_perimeter_with_mat3(const BMFace *f, const float mat3[3][3])
int BM_edge_face_count_at_most(const BMEdge *e, const int count_max)
int BM_vert_face_count(const BMVert *v)
int BM_edge_face_count(const BMEdge *e)
int BM_vert_edge_count(const BMVert *v)
float BM_edge_calc_face_angle_with_imat3(const BMEdge *e, const float imat3[3][3])
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert * v
bool add_as(ForwardKey &&key)
Definition BLI_set.hh:256
bool add(const Key &key)
Definition BLI_set.hh:248
bool is_empty() const
Definition BLI_set.hh:595
nullptr float
static int mesh_select_similar_compare_int(const int delta, const int compare)
static bool edbm_select_similar_poll_property(const bContext *, wmOperator *op, const PropertyRNA *prop)
static bool edge_data_value_set(BMEdge *edge, const int hflag, int *r_value)
static const EnumPropertyItem * select_similar_type_itemf(bContext *C, PointerRNA *, PropertyRNA *, bool *r_free)
void MESH_OT_select_similar(wmOperatorType *ot)
static float edge_length_squared_worldspace_get(Object *ob, BMEdge *edge)
static wmOperatorStatus edbm_select_similar_exec(bContext *C, wmOperator *op)
static void face_to_plane(const Object *ob, BMFace *face, float r_plane[4])
static void edge_pos_direction_worldspace_get(Object *ob, BMEdge *edge, float *r_dir)
static wmOperatorStatus similar_face_select_exec(bContext *C, wmOperator *op)
static const EnumPropertyItem prop_similar_compare_types[]
static wmOperatorStatus similar_edge_select_exec(bContext *C, wmOperator *op)
static wmOperatorStatus similar_vert_select_exec(bContext *C, wmOperator *op)
static bool face_data_value_set(BMFace *face, const int hflag, int *r_value)
uint pos
#define select(A, B, C)
float length(VecOp< float, D >) RET
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
const char * name
#define fabsf
float RNA_property_float_get(PointerRNA *ptr, PropertyRNA *prop)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
float RNA_float_get(PointerRNA *ptr, const char *name)
void RNA_property_float_set(PointerRNA *ptr, PropertyRNA *prop, float value)
int RNA_enum_get(PointerRNA *ptr, const char *name)
const char * RNA_property_identifier(const PropertyRNA *prop)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
void RNA_enum_item_end(EnumPropertyItem **items, int *totitem)
void RNA_def_property_translation_context(PropertyRNA *prop, const char *context)
void RNA_def_enum_funcs(PropertyRNA *prop, EnumPropertyItemFunc itemfunc)
void RNA_enum_items_add_value(EnumPropertyItem **items, int *totitem, const EnumPropertyItem *item, int value)
void RNA_def_property_ui_range(PropertyRNA *prop, double min, double max, double step, int precision)
BMVert * v1
BMVert * v2
short selectmode
short mat_nr
float no[3]
float co[3]
float no[3]
int totfacesel
int totvertsel
int totedgesel
struct MDeformWeight * dw
unsigned int def_nr
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
wmOperatorType * ot
Definition wm_files.cc:4237
wmOperatorStatus WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)