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