Blender V4.5
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 switch (type) {
214 case SIMFACE_MATERIAL: {
215 if (ob->totcol == 0) {
216 continue;
217 }
218 material_array = BKE_object_material_array_p(ob);
219 break;
220 }
221 case SIMFACE_FREESTYLE: {
223 face_data_value |= SIMFACE_DATA_FALSE;
224 continue;
225 }
226 break;
227 }
228 }
229
230 BMFace *face; /* Mesh face. */
231 BMIter iter; /* Selected faces iterator. */
232
233 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
235 switch (type) {
236 case SIMFACE_SIDES:
237 BLI_gset_add(gset, POINTER_FROM_INT(face->len));
238 break;
239 case SIMFACE_MATERIAL: {
240 Material *material = (*material_array)[face->mat_nr];
241 if (material != nullptr) {
242 BLI_gset_add(gset, material);
243 }
244 break;
245 }
246 case SIMFACE_AREA: {
247 float area = BM_face_calc_area_with_mat3(face, ob_m3);
248 BLI_kdtree_1d_insert(tree_1d, tree_index++, &area);
249 break;
250 }
251 case SIMFACE_PERIMETER: {
252 float perimeter = BM_face_calc_perimeter_with_mat3(face, ob_m3);
253 BLI_kdtree_1d_insert(tree_1d, tree_index++, &perimeter);
254 break;
255 }
256 case SIMFACE_NORMAL: {
257 float normal[3];
258 copy_v3_v3(normal, face->no);
259 mul_transposed_mat3_m4_v3(ob->world_to_object().ptr(), normal);
260 normalize_v3(normal);
261 BLI_kdtree_3d_insert(tree_3d, tree_index++, normal);
262 break;
263 }
264 case SIMFACE_COPLANAR: {
265 float plane[4];
266 face_to_plane(ob, face, plane);
267 BLI_kdtree_4d_insert(tree_4d, tree_index++, plane);
268 break;
269 }
270 case SIMFACE_SMOOTH: {
271 if (!face_data_value_set(face, BM_ELEM_SMOOTH, &face_data_value)) {
272 goto face_select_all;
273 }
274 break;
275 }
276 case SIMFACE_FREESTYLE: {
277 FreestyleFace *fface;
278 fface = static_cast<FreestyleFace *>(
280 if ((fface == nullptr) || ((fface->flag & FREESTYLE_FACE_MARK) == 0)) {
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 bool has_custom_data_layer = false;
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 has_custom_data_layer = CustomData_has_layer(&bm->pdata, CD_FREESTYLE_FACE);
331 if ((face_data_value == SIMFACE_DATA_TRUE) && !has_custom_data_layer) {
332 continue;
333 }
334 break;
335 }
336 }
337
338 BMFace *face; /* Mesh face. */
339 BMIter iter; /* Selected faces iterator. */
340
341 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
343 bool select = false;
344 switch (type) {
345 case SIMFACE_SIDES: {
346 const int num_sides = face->len;
347 GSetIterator gs_iter;
348 GSET_ITER (gs_iter, gset) {
349 const int num_sides_iter = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
350 const int delta_i = num_sides - num_sides_iter;
351 if (mesh_select_similar_compare_int(delta_i, compare)) {
352 select = true;
353 break;
354 }
355 }
356 break;
357 }
358 case SIMFACE_MATERIAL: {
359 const Material *material = (*material_array)[face->mat_nr];
360 if (material == nullptr) {
361 continue;
362 }
363
364 GSetIterator gs_iter;
365 GSET_ITER (gs_iter, gset) {
366 const Material *material_iter = static_cast<const Material *>(
367 BLI_gsetIterator_getKey(&gs_iter));
368 if (material == material_iter) {
369 select = true;
370 break;
371 }
372 }
373 break;
374 }
375 case SIMFACE_AREA: {
376 float area = BM_face_calc_area_with_mat3(face, ob_m3);
377 if (ED_select_similar_compare_float_tree(tree_1d, area, thresh, eSimilarCmp(compare)))
378 {
379 select = true;
380 }
381 break;
382 }
383 case SIMFACE_PERIMETER: {
384 float perimeter = BM_face_calc_perimeter_with_mat3(face, ob_m3);
386 tree_1d, perimeter, thresh, eSimilarCmp(compare)))
387 {
388 select = true;
389 }
390 break;
391 }
392 case SIMFACE_NORMAL: {
393 float normal[3];
394 copy_v3_v3(normal, face->no);
395 mul_transposed_mat3_m4_v3(ob->world_to_object().ptr(), normal);
396 normalize_v3(normal);
397
398 /* We are treating the normals as coordinates, the "nearest" one will
399 * also be the one closest to the angle. */
400 KDTreeNearest_3d nearest;
401 if (BLI_kdtree_3d_find_nearest(tree_3d, normal, &nearest) != -1) {
402 if (angle_normalized_v3v3(normal, nearest.co) <= thresh_radians) {
403 select = true;
404 }
405 }
406 break;
407 }
408 case SIMFACE_COPLANAR: {
409 float plane[4];
410 face_to_plane(ob, face, plane);
411
412 KDTreeNearest_4d nearest;
413 if (BLI_kdtree_4d_find_nearest(tree_4d, plane, &nearest) != -1) {
414 if (nearest.dist <= thresh) {
415 if ((fabsf(plane[3] - nearest.co[3]) <= thresh) &&
416 (angle_v3v3(plane, nearest.co) <= thresh_radians))
417 {
418 select = true;
419 }
420 }
421 }
422 break;
423 }
424 case SIMFACE_SMOOTH:
425 if ((BM_elem_flag_test(face, BM_ELEM_SMOOTH) != 0) ==
426 ((face_data_value & SIMFACE_DATA_TRUE) != 0))
427 {
428 select = true;
429 }
430 break;
431 case SIMFACE_FREESTYLE: {
432 FreestyleFace *fface;
433
434 if (!has_custom_data_layer) {
435 BLI_assert(face_data_value == SIMFACE_DATA_FALSE);
436 select = true;
437 break;
438 }
439
440 fface = static_cast<FreestyleFace *>(
442 if (((fface != nullptr) && (fface->flag & FREESTYLE_FACE_MARK)) ==
443 ((face_data_value & SIMFACE_DATA_TRUE) != 0))
444 {
445 select = true;
446 }
447 break;
448 }
449 }
450
451 if (select) {
452 BM_face_select_set(bm, face, true);
453 changed = true;
454 }
455 }
456 }
457
458 if (changed) {
461 params.calc_looptris = false;
462 params.calc_normals = false;
463 params.is_destructive = false;
464 EDBM_update(static_cast<Mesh *>(ob->data), &params);
465 }
466 }
467
468 if (false) {
469 face_select_all:
471
472 for (Object *ob : objects) {
474 BMesh *bm = em->bm;
475
476 BMFace *face; /* Mesh face. */
477 BMIter iter; /* Selected faces iterator. */
478
479 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
480 if (!BM_elem_flag_test(face, BM_ELEM_SELECT)) {
481 BM_face_select_set(bm, face, true);
482 }
483 }
486 params.calc_looptris = false;
487 params.calc_normals = false;
488 params.is_destructive = false;
489 EDBM_update(static_cast<Mesh *>(ob->data), &params);
490 }
491 }
492
493 BLI_kdtree_1d_free(tree_1d);
494 BLI_kdtree_3d_free(tree_3d);
495 BLI_kdtree_4d_free(tree_4d);
496 if (gset != nullptr) {
497 BLI_gset_free(gset, nullptr);
498 }
499
500 return OPERATOR_FINISHED;
501}
502
504
505/* -------------------------------------------------------------------- */
508
509static void edge_pos_direction_worldspace_get(Object *ob, BMEdge *edge, float *r_dir)
510{
511 float v1[3], v2[3];
512 copy_v3_v3(v1, edge->v1->co);
513 copy_v3_v3(v2, edge->v2->co);
514
515 mul_m4_v3(ob->object_to_world().ptr(), v1);
516 mul_m4_v3(ob->object_to_world().ptr(), v2);
517
518 sub_v3_v3v3(r_dir, v1, v2);
519 normalize_v3(r_dir);
520}
521
523{
524 float v1[3], v2[3];
525
526 mul_v3_mat3_m4v3(v1, ob->object_to_world().ptr(), edge->v1->co);
527 mul_v3_mat3_m4v3(v2, ob->object_to_world().ptr(), edge->v2->co);
528
529 return len_squared_v3v3(v1, v2);
530}
531
532enum {
537};
538
544static bool edge_data_value_set(BMEdge *edge, const int hflag, int *r_value)
545{
546 if (BM_elem_flag_test(edge, hflag)) {
547 *r_value |= SIMEDGE_DATA_TRUE;
548 }
549 else {
550 *r_value |= SIMEDGE_DATA_FALSE;
551 }
552
553 return *r_value != SIMEDGE_DATA_ALL;
554}
555
556/* TODO(dfelinto): `types` that should technically be compared in world space but are not:
557 * -SIMEDGE_FACE_ANGLE
558 */
560{
561 const Scene *scene = CTX_data_scene(C);
562 ViewLayer *view_layer = CTX_data_view_layer(C);
563
564 const int type = RNA_enum_get(op->ptr, "type");
565 const float thresh = RNA_float_get(op->ptr, "threshold");
566 const float thresh_radians = thresh * float(M_PI) + FLT_EPSILON;
567 const int compare = RNA_enum_get(op->ptr, "compare");
568
569 int tot_edges_selected_all = 0;
571 scene, view_layer, CTX_wm_view3d(C));
572
573 for (Object *ob : objects) {
575 tot_edges_selected_all += em->bm->totedgesel;
576 }
577
578 if (tot_edges_selected_all == 0) {
579 BKE_report(op->reports, RPT_ERROR, "No edge selected");
580 return OPERATOR_CANCELLED;
581 }
582
583 KDTree_1d *tree_1d = nullptr;
584 KDTree_3d *tree_3d = nullptr;
585 GSet *gset = nullptr;
586 int edge_data_value = SIMEDGE_DATA_NONE;
587
588 switch (type) {
589 case SIMEDGE_CREASE:
590 case SIMEDGE_BEVEL:
592 case SIMEDGE_LENGTH:
593 tree_1d = BLI_kdtree_1d_new(tot_edges_selected_all);
594 break;
595 case SIMEDGE_DIR:
596 tree_3d = BLI_kdtree_3d_new(tot_edges_selected_all * 2);
597 break;
598 case SIMEDGE_FACE:
599 gset = BLI_gset_ptr_new("Select similar edge: face");
600 break;
601 }
602
603 int tree_index = 0;
604 for (Object *ob : objects) {
606 BMesh *bm = em->bm;
607
608 if (bm->totedgesel == 0) {
609 continue;
610 }
611
612 switch (type) {
613 case SIMEDGE_FREESTYLE: {
615 edge_data_value |= SIMEDGE_DATA_FALSE;
616 continue;
617 }
618 break;
619 }
620 case SIMEDGE_CREASE: {
621 if (!CustomData_has_layer_named(&bm->edata, CD_PROP_FLOAT, "crease_edge")) {
622 float pos = 0.0f;
623 BLI_kdtree_1d_insert(tree_1d, tree_index++, &pos);
624 continue;
625 }
626 break;
627 }
628 case SIMEDGE_BEVEL: {
629 if (!CustomData_has_layer_named(&bm->edata, CD_PROP_FLOAT, "bevel_weight_edge")) {
630 float pos = 0.0f;
631 BLI_kdtree_1d_insert(tree_1d, tree_index++, &pos);
632 continue;
633 }
634 break;
635 }
636 }
637
638 int custom_data_offset;
639 switch (type) {
640 case SIMEDGE_CREASE:
641 custom_data_offset = CustomData_get_offset_named(&bm->edata, CD_PROP_FLOAT, "crease_edge");
642 break;
643 case SIMEDGE_BEVEL:
644 custom_data_offset = CustomData_get_offset_named(
645 &bm->edata, CD_PROP_FLOAT, "bevel_weight_edge");
646 break;
647 }
648
649 float ob_m3[3][3], ob_m3_inv[3][3];
650 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
651 invert_m3_m3(ob_m3_inv, ob_m3);
652
653 BMEdge *edge; /* Mesh edge. */
654 BMIter iter; /* Selected edges iterator. */
655
656 BM_ITER_MESH (edge, &iter, bm, BM_EDGES_OF_MESH) {
658 switch (type) {
659 case SIMEDGE_FACE:
661 break;
662 case SIMEDGE_DIR: {
663 float dir[3], dir_flip[3];
665 BLI_kdtree_3d_insert(tree_3d, tree_index++, dir);
666 /* Also store the flipped direction so it can be checked regardless of the verts order
667 * of the edges. */
668 negate_v3_v3(dir_flip, dir);
669 BLI_kdtree_3d_insert(tree_3d, tree_index++, dir_flip);
670 break;
671 }
672 case SIMEDGE_LENGTH: {
674 BLI_kdtree_1d_insert(tree_1d, tree_index++, &length);
675 break;
676 }
677 case SIMEDGE_FACE_ANGLE: {
678 if (BM_edge_face_count_at_most(edge, 2) == 2) {
679 float angle = BM_edge_calc_face_angle_with_imat3(edge, ob_m3_inv);
680 BLI_kdtree_1d_insert(tree_1d, tree_index++, &angle);
681 }
682 break;
683 }
684 case SIMEDGE_SEAM:
685 if (!edge_data_value_set(edge, BM_ELEM_SEAM, &edge_data_value)) {
686 goto edge_select_all;
687 }
688 break;
689 case SIMEDGE_SHARP:
690 if (!edge_data_value_set(edge, BM_ELEM_SMOOTH, &edge_data_value)) {
691 goto edge_select_all;
692 }
693 break;
694 case SIMEDGE_FREESTYLE: {
695 FreestyleEdge *fedge;
696 fedge = static_cast<FreestyleEdge *>(
698 if ((fedge == nullptr) || ((fedge->flag & FREESTYLE_EDGE_MARK) == 0)) {
699 edge_data_value |= SIMEDGE_DATA_FALSE;
700 }
701 else {
702 edge_data_value |= SIMEDGE_DATA_TRUE;
703 }
704 if (edge_data_value == SIMEDGE_DATA_ALL) {
705 goto edge_select_all;
706 }
707 break;
708 }
709 case SIMEDGE_CREASE:
710 case SIMEDGE_BEVEL: {
711 const float *value = BM_ELEM_CD_GET_FLOAT_P(edge, custom_data_offset);
712 BLI_kdtree_1d_insert(tree_1d, tree_index++, value);
713 break;
714 }
715 }
716 }
717 }
718 }
719
720 BLI_assert((type != SIMEDGE_FREESTYLE) || (edge_data_value != SIMEDGE_DATA_NONE));
721
722 if (tree_1d != nullptr) {
723 BLI_kdtree_1d_deduplicate(tree_1d);
724 BLI_kdtree_1d_balance(tree_1d);
725 }
726 if (tree_3d != nullptr) {
727 BLI_kdtree_3d_deduplicate(tree_3d);
728 BLI_kdtree_3d_balance(tree_3d);
729 }
730
731 for (Object *ob : objects) {
733 BMesh *bm = em->bm;
734 bool changed = false;
735
736 bool has_custom_data_layer = false;
737 switch (type) {
738 case SIMEDGE_FREESTYLE: {
739 has_custom_data_layer = CustomData_has_layer(&bm->edata, CD_FREESTYLE_EDGE);
740 if ((edge_data_value == SIMEDGE_DATA_TRUE) && !has_custom_data_layer) {
741 continue;
742 }
743 break;
744 }
745 case SIMEDGE_CREASE: {
746 has_custom_data_layer = CustomData_has_layer_named(
747 &bm->edata, CD_PROP_FLOAT, "crease_edge");
748 if (!has_custom_data_layer) {
749 /* Proceed only if we have to select all the edges that have custom data value of 0.0f.
750 * In this case we will just select all the edges.
751 * Otherwise continue the for loop. */
752 if (!ED_select_similar_compare_float_tree(tree_1d, 0.0f, thresh, eSimilarCmp(compare))) {
753 continue;
754 }
755 }
756 break;
757 }
758 case SIMEDGE_BEVEL: {
759 has_custom_data_layer = CustomData_has_layer_named(
760 &bm->edata, CD_PROP_FLOAT, "bevel_weight_edge");
761 if (!has_custom_data_layer) {
762 /* Proceed only if we have to select all the edges that have custom data value of 0.0f.
763 * In this case we will just select all the edges.
764 * Otherwise continue the for loop. */
765 if (!ED_select_similar_compare_float_tree(tree_1d, 0.0f, thresh, eSimilarCmp(compare))) {
766 continue;
767 }
768 }
769 break;
770 }
771 }
772
773 float ob_m3[3][3], ob_m3_inv[3][3];
774 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
775 invert_m3_m3(ob_m3_inv, ob_m3);
776
777 int custom_data_offset;
778 switch (type) {
779 case SIMEDGE_CREASE:
780 custom_data_offset = CustomData_get_offset_named(&bm->edata, CD_PROP_FLOAT, "crease_edge");
781 break;
782 case SIMEDGE_BEVEL:
783 custom_data_offset = CustomData_get_offset_named(
784 &bm->edata, CD_PROP_FLOAT, "bevel_weight_edge");
785 break;
786 }
787
788 BMEdge *edge; /* Mesh edge. */
789 BMIter iter; /* Selected edges iterator. */
790
791 BM_ITER_MESH (edge, &iter, bm, BM_EDGES_OF_MESH) {
793 bool select = false;
794 switch (type) {
795 case SIMEDGE_FACE: {
796 const int num_faces = BM_edge_face_count(edge);
797 GSetIterator gs_iter;
798 GSET_ITER (gs_iter, gset) {
799 const int num_faces_iter = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
800 const int delta_i = num_faces - num_faces_iter;
801 if (mesh_select_similar_compare_int(delta_i, compare)) {
802 select = true;
803 break;
804 }
805 }
806 break;
807 }
808 case SIMEDGE_DIR: {
809 float dir[3];
811
812 /* We are treating the direction as coordinates, the "nearest" one will
813 * also be the one closest to the intended direction. */
814 KDTreeNearest_3d nearest;
815 if (BLI_kdtree_3d_find_nearest(tree_3d, dir, &nearest) != -1) {
816 if (angle_normalized_v3v3(dir, nearest.co) <= thresh_radians) {
817 select = true;
818 }
819 }
820 break;
821 }
822 case SIMEDGE_LENGTH: {
825 tree_1d, length, thresh, eSimilarCmp(compare)))
826 {
827 select = true;
828 }
829 break;
830 }
831 case SIMEDGE_FACE_ANGLE: {
832 if (BM_edge_face_count_at_most(edge, 2) == 2) {
833 float angle = BM_edge_calc_face_angle_with_imat3(edge, ob_m3_inv);
835 select = true;
836 }
837 }
838 break;
839 }
840 case SIMEDGE_SEAM:
841 if ((BM_elem_flag_test(edge, BM_ELEM_SEAM) != 0) ==
842 ((edge_data_value & SIMEDGE_DATA_TRUE) != 0))
843 {
844 select = true;
845 }
846 break;
847 case SIMEDGE_SHARP:
848 if ((BM_elem_flag_test(edge, BM_ELEM_SMOOTH) != 0) ==
849 ((edge_data_value & SIMEDGE_DATA_TRUE) != 0))
850 {
851 select = true;
852 }
853 break;
854 case SIMEDGE_FREESTYLE: {
855 FreestyleEdge *fedge;
856
857 if (!has_custom_data_layer) {
858 BLI_assert(edge_data_value == SIMEDGE_DATA_FALSE);
859 select = true;
860 break;
861 }
862
863 fedge = static_cast<FreestyleEdge *>(
865 if (((fedge != nullptr) && (fedge->flag & FREESTYLE_EDGE_MARK)) ==
866 ((edge_data_value & SIMEDGE_DATA_TRUE) != 0))
867 {
868 select = true;
869 }
870 break;
871 }
872 case SIMEDGE_CREASE:
873 case SIMEDGE_BEVEL: {
874 if (!has_custom_data_layer) {
875 select = true;
876 break;
877 }
878
879 const float *value = BM_ELEM_CD_GET_FLOAT_P(edge, custom_data_offset);
881 tree_1d, *value, thresh, eSimilarCmp(compare)))
882 {
883 select = true;
884 }
885 break;
886 }
887 }
888
889 if (select) {
890 BM_edge_select_set(bm, edge, true);
891 changed = true;
892 }
893 }
894 }
895
896 if (changed) {
899 params.calc_looptris = false;
900 params.calc_normals = false;
901 params.is_destructive = false;
902 EDBM_update(static_cast<Mesh *>(ob->data), &params);
903 }
904 }
905
906 if (false) {
907 edge_select_all:
909
910 for (Object *ob : objects) {
912 BMesh *bm = em->bm;
913
914 BMEdge *edge; /* Mesh edge. */
915 BMIter iter; /* Selected edges iterator. */
916
917 BM_ITER_MESH (edge, &iter, bm, BM_EDGES_OF_MESH) {
918 if (!BM_elem_flag_test(edge, BM_ELEM_SELECT)) {
919 BM_edge_select_set(bm, edge, true);
920 }
921 }
924 params.calc_looptris = false;
925 params.calc_normals = false;
926 params.is_destructive = false;
927 EDBM_update(static_cast<Mesh *>(ob->data), &params);
928 }
929 }
930
931 BLI_kdtree_1d_free(tree_1d);
932 BLI_kdtree_3d_free(tree_3d);
933 if (gset != nullptr) {
934 BLI_gset_free(gset, nullptr);
935 }
936
937 return OPERATOR_FINISHED;
938}
939
941
942/* -------------------------------------------------------------------- */
945
947{
948 const Scene *scene = CTX_data_scene(C);
949 ViewLayer *view_layer = CTX_data_view_layer(C);
950
951 /* get the type from RNA */
952 const int type = RNA_enum_get(op->ptr, "type");
953 const float thresh = RNA_float_get(op->ptr, "threshold");
954 const float thresh_radians = thresh * float(M_PI) + FLT_EPSILON;
955 const int compare = RNA_enum_get(op->ptr, "compare");
956
957 int tot_verts_selected_all = 0;
959 scene, view_layer, CTX_wm_view3d(C));
960
961 for (Object *ob : objects) {
963 tot_verts_selected_all += em->bm->totvertsel;
964 }
965
966 if (tot_verts_selected_all == 0) {
967 BKE_report(op->reports, RPT_ERROR, "No vertex selected");
968 return OPERATOR_CANCELLED;
969 }
970
971 KDTree_3d *tree_3d = nullptr;
972 KDTree_1d *tree_1d = nullptr;
973 blender::Set<blender::StringRef> selected_vertex_groups;
974 blender::Set<int> connected_elems_num_set;
975
976 switch (type) {
977 case SIMVERT_NORMAL:
978 tree_3d = BLI_kdtree_3d_new(tot_verts_selected_all);
979 break;
980 case SIMVERT_CREASE:
981 tree_1d = BLI_kdtree_1d_new(tot_verts_selected_all);
982 break;
983 case SIMVERT_EDGE:
984 case SIMVERT_FACE:
985 case SIMVERT_VGROUP:
986 break;
987 }
988
989 int normal_tree_index = 0;
990 int tree_1d_index = 0;
991 for (Object *ob : objects) {
993 BMesh *bm = em->bm;
994 int cd_dvert_offset = -1;
995 int cd_crease_offset = -1;
996 BLI_bitmap *defbase_selected = nullptr;
997 int defbase_len = 0;
998
999 invert_m4_m4(ob->runtime->world_to_object.ptr(), ob->object_to_world().ptr());
1000
1001 if (bm->totvertsel == 0) {
1002 continue;
1003 }
1004
1005 if (type == SIMVERT_VGROUP) {
1006 cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT);
1007 if (cd_dvert_offset == -1) {
1008 continue;
1009 }
1010 defbase_len = BKE_object_defgroup_count(ob);
1011 if (defbase_len == 0) {
1012 continue;
1013 }
1014 defbase_selected = BLI_BITMAP_NEW(defbase_len, __func__);
1015 }
1016 else if (type == SIMVERT_CREASE) {
1017 if (!CustomData_has_layer_named(&bm->vdata, CD_PROP_FLOAT, "crease_vert")) {
1018 float pos = 0.0f;
1019 BLI_kdtree_1d_insert(tree_1d, tree_1d_index++, &pos);
1020 continue;
1021 }
1022 cd_crease_offset = CustomData_get_offset_named(&bm->vdata, CD_PROP_FLOAT, "crease_vert");
1023 }
1024
1025 BMVert *vert; /* Mesh vertex. */
1026 BMIter iter; /* Selected verts iterator. */
1027
1028 BM_ITER_MESH (vert, &iter, bm, BM_VERTS_OF_MESH) {
1029 if (BM_elem_flag_test(vert, BM_ELEM_SELECT)) {
1030 switch (type) {
1031 case SIMVERT_FACE:
1032 connected_elems_num_set.add(BM_vert_face_count(vert));
1033 break;
1034 case SIMVERT_EDGE:
1035 connected_elems_num_set.add(BM_vert_edge_count(vert));
1036 break;
1037 case SIMVERT_NORMAL: {
1038 float normal[3];
1039 copy_v3_v3(normal, vert->no);
1040 mul_transposed_mat3_m4_v3(ob->world_to_object().ptr(), normal);
1041 normalize_v3(normal);
1042
1043 BLI_kdtree_3d_insert(tree_3d, normal_tree_index++, normal);
1044 break;
1045 }
1046 case SIMVERT_VGROUP: {
1047 MDeformVert *dvert = static_cast<MDeformVert *>(
1048 BM_ELEM_CD_GET_VOID_P(vert, cd_dvert_offset));
1049 MDeformWeight *dw = dvert->dw;
1050
1051 for (int i = 0; i < dvert->totweight; i++, dw++) {
1052 if (dw->weight > 0.0f) {
1053 if (LIKELY(dw->def_nr < defbase_len)) {
1054 BLI_BITMAP_ENABLE(defbase_selected, dw->def_nr);
1055 }
1056 }
1057 }
1058 break;
1059 }
1060 case SIMVERT_CREASE: {
1061 const float *value = BM_ELEM_CD_GET_FLOAT_P(vert, cd_crease_offset);
1062 BLI_kdtree_1d_insert(tree_1d, tree_1d_index++, value);
1063 break;
1064 }
1065 }
1066 }
1067 }
1068
1069 if (type == SIMVERT_VGROUP) {
1070 /* We store the names of the vertex groups, so we can select
1071 * vertex groups with the same name in different objects. */
1072
1073 const ListBase *defbase = BKE_object_defgroup_list(ob);
1074
1075 int i = 0;
1076 LISTBASE_FOREACH (bDeformGroup *, dg, defbase) {
1077 if (BLI_BITMAP_TEST(defbase_selected, i)) {
1078 selected_vertex_groups.add_as(dg->name);
1079 }
1080 i += 1;
1081 }
1082 MEM_freeN(defbase_selected);
1083 }
1084 }
1085
1086 if (type == SIMVERT_VGROUP) {
1087 if (selected_vertex_groups.is_empty()) {
1088 BKE_report(op->reports, RPT_INFO, "No vertex group among the selected vertices");
1089 }
1090 }
1091
1092 /* Remove duplicated entries. */
1093 if (tree_1d != nullptr) {
1094 BLI_kdtree_1d_deduplicate(tree_1d);
1095 BLI_kdtree_1d_balance(tree_1d);
1096 }
1097 if (tree_3d != nullptr) {
1098 BLI_kdtree_3d_deduplicate(tree_3d);
1099 BLI_kdtree_3d_balance(tree_3d);
1100 }
1101
1102 /* Run the matching operations. */
1103 for (Object *ob : objects) {
1105 BMesh *bm = em->bm;
1106 bool changed = false;
1107 bool has_crease_layer = false;
1108 int cd_dvert_offset = -1;
1109 int cd_crease_offset = -1;
1110 BLI_bitmap *defbase_selected = nullptr;
1111 int defbase_len = 0;
1112
1113 if (type == SIMVERT_VGROUP) {
1114 cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT);
1115 if (cd_dvert_offset == -1) {
1116 continue;
1117 }
1118 const ListBase *defbase = BKE_object_defgroup_list(ob);
1119 defbase_len = BLI_listbase_count(defbase);
1120 if (defbase_len == 0) {
1121 continue;
1122 }
1123
1124 /* We map back the names of the vertex groups to their corresponding indices
1125 * for this object. This is fast, and keep the logic for each vertex very simple. */
1126
1127 defbase_selected = BLI_BITMAP_NEW(defbase_len, __func__);
1128 bool found_any = false;
1129 for (const blender::StringRef name : selected_vertex_groups) {
1130 int vgroup_id = BKE_defgroup_name_index(defbase, name);
1131 if (vgroup_id != -1) {
1132 BLI_BITMAP_ENABLE(defbase_selected, vgroup_id);
1133 found_any = true;
1134 }
1135 }
1136 if (found_any == false) {
1137 MEM_freeN(defbase_selected);
1138 continue;
1139 }
1140 }
1141 else if (type == SIMVERT_CREASE) {
1142 cd_crease_offset = CustomData_get_offset_named(&bm->vdata, CD_PROP_FLOAT, "crease_vert");
1143 has_crease_layer = CustomData_has_layer_named(&bm->vdata, CD_PROP_FLOAT, "crease_vert");
1144 if (!has_crease_layer) {
1145 /* Proceed only if we have to select all the vertices that have custom data value of 0.0f.
1146 * In this case we will just select all the vertices.
1147 * Otherwise continue the for loop. */
1148 if (!ED_select_similar_compare_float_tree(tree_1d, 0.0f, thresh, eSimilarCmp(compare))) {
1149 continue;
1150 }
1151 }
1152 }
1153
1154 BMVert *vert; /* Mesh vertex. */
1155 BMIter iter; /* Selected verts iterator. */
1156
1157 BM_ITER_MESH (vert, &iter, bm, BM_VERTS_OF_MESH) {
1159 bool select = false;
1160 switch (type) {
1161 case SIMVERT_EDGE: {
1162 const int num_edges = BM_vert_edge_count(vert);
1163 for (const int num_edges_iter : connected_elems_num_set) {
1164 const int delta_i = num_edges - num_edges_iter;
1165 if (mesh_select_similar_compare_int(delta_i, compare)) {
1166 select = true;
1167 break;
1168 }
1169 }
1170 break;
1171 }
1172 case SIMVERT_FACE: {
1173 const int num_faces = BM_vert_face_count(vert);
1174 for (const int num_faces_iter : connected_elems_num_set) {
1175 const int delta_i = num_faces - num_faces_iter;
1176 if (mesh_select_similar_compare_int(delta_i, compare)) {
1177 select = true;
1178 break;
1179 }
1180 }
1181 break;
1182 }
1183 case SIMVERT_NORMAL: {
1184 float normal[3];
1185 copy_v3_v3(normal, vert->no);
1186 mul_transposed_mat3_m4_v3(ob->world_to_object().ptr(), normal);
1187 normalize_v3(normal);
1188
1189 /* We are treating the normals as coordinates, the "nearest" one will
1190 * also be the one closest to the angle. */
1191 KDTreeNearest_3d nearest;
1192 if (BLI_kdtree_3d_find_nearest(tree_3d, normal, &nearest) != -1) {
1193 if (angle_normalized_v3v3(normal, nearest.co) <= thresh_radians) {
1194 select = true;
1195 }
1196 }
1197 break;
1198 }
1199 case SIMVERT_VGROUP: {
1200 MDeformVert *dvert = static_cast<MDeformVert *>(
1201 BM_ELEM_CD_GET_VOID_P(vert, cd_dvert_offset));
1202 MDeformWeight *dw = dvert->dw;
1203
1204 for (int i = 0; i < dvert->totweight; i++, dw++) {
1205 if (dw->weight > 0.0f) {
1206 if (LIKELY(dw->def_nr < defbase_len)) {
1207 if (BLI_BITMAP_TEST(defbase_selected, dw->def_nr)) {
1208 select = true;
1209 break;
1210 }
1211 }
1212 }
1213 }
1214 break;
1215 }
1216 case SIMVERT_CREASE: {
1217 if (!has_crease_layer) {
1218 select = true;
1219 break;
1220 }
1221 const float *value = BM_ELEM_CD_GET_FLOAT_P(vert, cd_crease_offset);
1223 tree_1d, *value, thresh, eSimilarCmp(compare)))
1224 {
1225 select = true;
1226 }
1227 break;
1228 }
1229 }
1230
1231 if (select) {
1232 BM_vert_select_set(bm, vert, true);
1233 changed = true;
1234 }
1235 }
1236 }
1237
1238 if (type == SIMVERT_VGROUP) {
1239 MEM_freeN(defbase_selected);
1240 }
1241
1242 if (changed) {
1245 params.calc_looptris = false;
1246 params.calc_normals = false;
1247 params.is_destructive = false;
1248 EDBM_update(static_cast<Mesh *>(ob->data), &params);
1249 }
1250 }
1251
1252 BLI_kdtree_1d_free(tree_1d);
1253 BLI_kdtree_3d_free(tree_3d);
1254
1255 return OPERATOR_FINISHED;
1256}
1257
1259
1260/* -------------------------------------------------------------------- */
1263
1265{
1267 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "threshold");
1268
1269 const int type = RNA_enum_get(op->ptr, "type");
1270
1271 if (!RNA_property_is_set(op->ptr, prop)) {
1273 }
1274 else {
1275 ts->select_thresh = RNA_property_float_get(op->ptr, prop);
1276 }
1277
1278 if (type < 100) {
1279 return similar_vert_select_exec(C, op);
1280 }
1281 if (type < 200) {
1282 return similar_edge_select_exec(C, op);
1283 }
1284 return similar_face_select_exec(C, op);
1285}
1286
1288 PointerRNA * /*ptr*/,
1289 PropertyRNA * /*prop*/,
1290 bool *r_free)
1291{
1292 Object *obedit;
1293
1294 if (!C) { /* needed for docs and i18n tools */
1295 return prop_similar_types;
1296 }
1297
1298 obedit = CTX_data_edit_object(C);
1299
1300 if (obedit && obedit->type == OB_MESH) {
1301 EnumPropertyItem *item = nullptr;
1302 int a, totitem = 0;
1304
1305 if (em->selectmode & SCE_SELECT_VERTEX) {
1306 for (a = SIMVERT_NORMAL; a < SIMEDGE_LENGTH; a++) {
1307 RNA_enum_items_add_value(&item, &totitem, prop_similar_types, a);
1308 }
1309 }
1310 else if (em->selectmode & SCE_SELECT_EDGE) {
1311 for (a = SIMEDGE_LENGTH; a < SIMFACE_MATERIAL; a++) {
1312 RNA_enum_items_add_value(&item, &totitem, prop_similar_types, a);
1313 }
1314 }
1315 else if (em->selectmode & SCE_SELECT_FACE) {
1316#ifdef WITH_FREESTYLE
1317 const int a_end = SIMFACE_FREESTYLE;
1318#else
1319 const int a_end = SIMFACE_SMOOTH;
1320#endif
1321 for (a = SIMFACE_MATERIAL; a <= a_end; a++) {
1322 RNA_enum_items_add_value(&item, &totitem, prop_similar_types, a);
1323 }
1324 }
1325 RNA_enum_item_end(&item, &totitem);
1326
1327 *r_free = true;
1328
1329 return item;
1330 }
1331
1332 return prop_similar_types;
1333}
1334
1336 wmOperator *op,
1337 const PropertyRNA *prop)
1338{
1339 const char *prop_id = RNA_property_identifier(prop);
1340 const int type = RNA_enum_get(op->ptr, "type");
1341
1342 /* Only show compare when it is used. */
1343 if (STREQ(prop_id, "compare")) {
1344 if (type == SIMVERT_VGROUP) {
1345 return false;
1346 }
1347 }
1348 /* Only show threshold when it is used. */
1349 else if (STREQ(prop_id, "threshold")) {
1350 if (!ELEM(type,
1361 {
1362 return false;
1363 }
1364 }
1365
1366 return true;
1367}
1368
1370{
1371 PropertyRNA *prop;
1372
1373 /* identifiers */
1374 ot->name = "Select Similar";
1375 ot->idname = "MESH_OT_select_similar";
1376 ot->description = "Select similar vertices, edges or faces by property types";
1377
1378 /* API callbacks. */
1379 ot->invoke = WM_menu_invoke;
1381 ot->poll = ED_operator_editmesh;
1382 ot->poll_property = edbm_select_similar_poll_property;
1383
1384 /* flags */
1385 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1386
1387 /* properties */
1388 prop = ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, SIMVERT_NORMAL, "Type", "");
1391
1392 RNA_def_enum(ot->srna, "compare", prop_similar_compare_types, SIM_CMP_EQ, "Compare", "");
1393
1394 prop = RNA_def_float(ot->srna, "threshold", 0.0f, 0.0f, 100000.0f, "Threshold", "", 0.0f, 1.0f);
1395 /* Very small values are needed sometimes, similar area of small faces for e.g: see #87823 */
1396 RNA_def_property_ui_range(prop, 0.0, 1.0, 0.01, 5);
1397}
1398
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)
void * CustomData_bmesh_get(const CustomData *data, void *block, eCustomDataType type)
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
support for deformation groups and hooks.
int BKE_object_defgroup_count(const Object *ob)
Definition deform.cc:591
int BKE_defgroup_name_index(const ListBase *defbase, blender::StringRef name)
Definition deform.cc:529
const ListBase * BKE_object_defgroup_list(const Object *ob)
Definition deform.cc:574
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)
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
#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
@ CD_FREESTYLE_EDGE
@ CD_FREESTYLE_FACE
@ FREESTYLE_EDGE_MARK
@ FREESTYLE_FACE_MARK
@ OB_MESH
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
void EDBM_update(Mesh *mesh, const EDBMUpdate_Params *params)
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[]
@ BM_ELEM_HIDDEN
@ BM_ELEM_SEAM
@ BM_ELEM_SELECT
@ BM_ELEM_SMOOTH
#define BM_FACE_FIRST_LOOP(p)
#define BM_ELEM_CD_GET_FLOAT_P(ele, offset)
#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
#define fabsf(x)
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
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)
BMHeader head
BMVert * v1
BMVert * v2
short selectmode
short mat_nr
BMHeader head
float no[3]
void * data
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:4226
wmOperatorStatus WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)