Blender V4.5
editmesh_utils.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 "DNA_key_types.h"
12#include "DNA_mesh_types.h"
13#include "DNA_object_types.h"
14
15#include "BLI_array.hh"
16#include "BLI_kdtree.h"
17#include "BLI_listbase.h"
18#include "BLI_math_matrix.h"
19#include "BLI_math_vector.h"
20
21#include "BKE_context.hh"
22#include "BKE_customdata.hh"
23#include "BKE_editmesh.hh"
24#include "BKE_editmesh_bvh.hh"
25#include "BKE_layer.hh"
26#include "BKE_mesh.hh"
27#include "BKE_mesh_mapping.hh"
28#include "BKE_report.hh"
29
30#include "DEG_depsgraph.hh"
31
32#include "WM_api.hh"
33#include "WM_types.hh"
34
35#include "ED_mesh.hh"
36#include "ED_screen.hh"
38#include "ED_uvedit.hh"
39#include "ED_view3d.hh"
40
41#include "mesh_intern.hh" /* own include */
42
43using blender::Vector;
44
45/* -------------------------------------------------------------------- */
48
49/* Mesh backup implementation.
50 * This would greatly benefit from some sort of binary diffing
51 * just as the undo stack would.
52 * So leaving this as an interface for further work */
53
55{
56 BMBackup backup;
57 backup.bmcopy = BM_mesh_copy(em->bm);
58 return backup;
59}
60
61void EDBM_redo_state_restore(BMBackup *backup, BMEditMesh *em, bool recalc_looptris)
62{
64 BMesh *tmpbm = BM_mesh_copy(backup->bmcopy);
65 *em->bm = *tmpbm;
66 MEM_freeN(tmpbm);
67 tmpbm = nullptr;
68
69 if (recalc_looptris) {
71 }
72}
73
74void EDBM_redo_state_restore_and_free(BMBackup *backup, BMEditMesh *em, bool recalc_looptris)
75{
77 *em->bm = *backup->bmcopy;
78 MEM_freeN(backup->bmcopy);
79 backup->bmcopy = nullptr;
80 if (recalc_looptris) {
82 }
83}
84
86{
87 if (backup->bmcopy) {
89 MEM_freeN(backup->bmcopy);
90 }
91}
92
94
95/* -------------------------------------------------------------------- */
98
99bool EDBM_op_init(BMEditMesh *em, BMOperator *bmop, wmOperator *op, const char *fmt, ...)
100{
101 BMesh *bm = em->bm;
102 va_list list;
103
104 va_start(list, fmt);
105
106 if (!BMO_op_vinitf(bm, bmop, BMO_FLAG_DEFAULTS, fmt, list)) {
107 BKE_reportf(op->reports, RPT_ERROR, "Parse error in %s", __func__);
108 va_end(list);
109 return false;
110 }
111
112 va_end(list);
113
114 return true;
115}
116
117bool EDBM_op_finish(BMEditMesh *em, BMOperator *bmop, wmOperator *op, const bool do_report)
118{
119 const char *errmsg;
120
121#ifndef NDEBUG
122 struct StatePrev {
123 int verts_len, edges_len, loops_len, faces_len;
124 };
125 StatePrev em_state_prev = {
126 em->bm->totvert,
127 em->bm->totedge,
128 em->bm->totloop,
129 em->bm->totface,
130 };
131#endif
132
133 BMO_op_finish(em->bm, bmop);
134
135 bool changed = false;
136 bool changed_was_set = false;
137
138 eBMOpErrorLevel level;
139 while (BMO_error_pop(em->bm, &errmsg, nullptr, &level)) {
140 eReportType type = RPT_INFO;
141 switch (level) {
142 case BMO_ERROR_CANCEL: {
143 changed_was_set = true;
144 break;
145 }
146 case BMO_ERROR_WARN: {
147 type = RPT_WARNING;
148 changed_was_set = true;
149 changed = true;
150 break;
151 }
152 case BMO_ERROR_FATAL: {
153 type = RPT_ERROR;
154 changed_was_set = true;
155 changed = true;
156 break;
157 }
158 }
159
160 if (do_report) {
161 BKE_report(op->reports, type, errmsg);
162 }
163 }
164 if (changed_was_set == false) {
165 changed = true;
166 }
167
168#ifndef NDEBUG
169 if (changed == false) {
170 BLI_assert((em_state_prev.verts_len == em->bm->totvert) &&
171 (em_state_prev.edges_len == em->bm->totedge) &&
172 (em_state_prev.loops_len == em->bm->totloop) &&
173 (em_state_prev.faces_len == em->bm->totface));
174 }
175#endif
176
177 return changed;
178}
179
180bool EDBM_op_callf(BMEditMesh *em, wmOperator *op, const char *fmt, ...)
181{
182 BMesh *bm = em->bm;
183 BMOperator bmop;
184 va_list list;
185
186 va_start(list, fmt);
187
188 if (!BMO_op_vinitf(bm, &bmop, BMO_FLAG_DEFAULTS, fmt, list)) {
189 BKE_reportf(op->reports, RPT_ERROR, "Parse error in %s", __func__);
190 va_end(list);
191 return false;
192 }
193
194 BMO_op_exec(bm, &bmop);
195
196 va_end(list);
197 return EDBM_op_finish(em, &bmop, op, true);
198}
199
201 wmOperator *op,
202 const char *select_slot_out,
203 const bool select_extend,
204 const char *fmt,
205 ...)
206{
207 BMesh *bm = em->bm;
208 BMOperator bmop;
209 va_list list;
210
211 va_start(list, fmt);
212
213 if (!BMO_op_vinitf(bm, &bmop, BMO_FLAG_DEFAULTS, fmt, list)) {
214 BKE_reportf(op->reports, RPT_ERROR, "Parse error in %s", __func__);
215 va_end(list);
216 return false;
217 }
218
219 BMO_op_exec(bm, &bmop);
220
221 BMOpSlot *slot_select_out = BMO_slot_get(bmop.slots_out, select_slot_out);
222 char hflag = slot_select_out->slot_subtype.elem & BM_ALL_NOLOOP;
223 BLI_assert(hflag != 0);
224
225 if (select_extend == false) {
227 }
228
230 em->bm, bmop.slots_out, select_slot_out, hflag, BM_ELEM_SELECT, true);
231
232 va_end(list);
233 return EDBM_op_finish(em, &bmop, op, true);
234}
235
236bool EDBM_op_call_silentf(BMEditMesh *em, const char *fmt, ...)
237{
238 BMesh *bm = em->bm;
239 BMOperator bmop;
240 va_list list;
241
242 va_start(list, fmt);
243
244 if (!BMO_op_vinitf(bm, &bmop, BMO_FLAG_DEFAULTS, fmt, list)) {
245 va_end(list);
246 return false;
247 }
248
249 BMO_op_exec(bm, &bmop);
250
251 va_end(list);
252 return EDBM_op_finish(em, &bmop, nullptr, false);
253}
254
256
257/* -------------------------------------------------------------------- */
262
274{
275 const Mesh *mesh = static_cast<const Mesh *>(ob->data);
276 if (UNLIKELY((ob->shapenr == 0) && (mesh->key && !BLI_listbase_is_empty(&mesh->key->block)))) {
277 return 1;
278 }
279 return ob->shapenr;
280}
281
282void EDBM_mesh_make(Object *ob, const int select_mode, const bool add_key_index)
283{
284 Mesh *mesh = static_cast<Mesh *>(ob->data);
285 EDBM_mesh_make_from_mesh(ob, mesh, select_mode, add_key_index);
286}
287
289 Mesh *src_mesh,
290 const int select_mode,
291 const bool add_key_index)
292{
293 Mesh *mesh = static_cast<Mesh *>(ob->data);
294 BMeshCreateParams create_params{};
295 create_params.use_toolflags = true;
296 /* Clamp the index, so the behavior of enter & exit edit-mode matches, see #43998. */
297 const int shapenr = object_shapenr_basis_index_ensured(ob);
298
299 BMesh *bm = BKE_mesh_to_bmesh(src_mesh, shapenr, add_key_index, &create_params);
300
301 if (mesh->runtime->edit_mesh) {
302 /* this happens when switching shape keys */
303 EDBM_mesh_free_data(mesh->runtime->edit_mesh.get());
304 mesh->runtime->edit_mesh.reset();
305 }
306
307 /* Executing operators re-tessellates,
308 * so we can avoid doing here but at some point it may need to be added back. */
309 mesh->runtime->edit_mesh = std::make_shared<BMEditMesh>();
310 mesh->runtime->edit_mesh->bm = bm;
311
312 mesh->runtime->edit_mesh->selectmode = mesh->runtime->edit_mesh->bm->selectmode = select_mode;
313 mesh->runtime->edit_mesh->mat_nr = (ob->actcol > 0) ? ob->actcol - 1 : 0;
314
315 /* we need to flush selection because the mode may have changed from when last in editmode */
316 EDBM_selectmode_flush(mesh->runtime->edit_mesh.get());
317}
318
319void EDBM_mesh_load_ex(Main *bmain, Object *ob, bool free_data)
320{
321 Mesh *mesh = static_cast<Mesh *>(ob->data);
322 BMesh *bm = mesh->runtime->edit_mesh->bm;
323
324 /* Workaround for #42360, 'ob->shapenr' should be 1 in this case.
325 * however this isn't synchronized between objects at the moment. */
326 if (UNLIKELY((ob->shapenr == 0) && (object_shapenr_basis_index_ensured(ob) > 0))) {
327 bm->shapenr = 1;
328 }
329
331 params.calc_object_remap = true;
332 params.update_shapekey_indices = !free_data;
333 BM_mesh_bm_to_me(bmain, bm, mesh, &params);
334}
335
336void EDBM_mesh_load(Main *bmain, Object *ob)
337{
338 EDBM_mesh_load_ex(bmain, ob, true);
339}
340
342{
343 /* These tables aren't used yet, so it's not strictly necessary
344 * to 'end' them but if someone tries to start using them,
345 * having these in place will save a lot of pain. */
348
350}
351
353
354/* -------------------------------------------------------------------- */
357
359{
360 Scene *scene = CTX_data_scene(C);
361 Object *obedit = CTX_data_edit_object(C);
363
364 if (!em) {
365 return;
366 }
367
368 scene->toolsettings->selectmode = em->selectmode;
369
370 /* Request redraw of header buttons (to show new select mode) */
372}
373
374void EDBM_selectmode_flush_ex(BMEditMesh *em, const short selectmode)
375{
377}
378
383
385{
386 /* function below doesn't use. just do this to keep the values in sync */
387 em->bm->selectmode = em->selectmode;
389}
390
392{
393 /* function below doesn't use. just do this to keep the values in sync */
394 em->bm->selectmode = em->selectmode;
396}
397
398void EDBM_select_more(BMEditMesh *em, const bool use_face_step)
399{
400 BMOperator bmop;
401 const bool use_faces = (em->selectmode == SCE_SELECT_FACE);
402
403 BMO_op_initf(em->bm,
404 &bmop,
406 "region_extend geom=%hvef use_contract=%b use_faces=%b use_face_step=%b",
408 false,
409 use_faces,
410 use_face_step);
411 BMO_op_exec(em->bm, &bmop);
412 /* Don't flush selection in edge/vertex mode. */
414 em->bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, use_faces ? true : false);
415 BMO_op_finish(em->bm, &bmop);
416
418}
419
420void EDBM_select_less(BMEditMesh *em, const bool use_face_step)
421{
422 BMOperator bmop;
423 const bool use_faces = (em->selectmode == SCE_SELECT_FACE);
424
425 BMO_op_initf(em->bm,
426 &bmop,
428 "region_extend geom=%hvef use_contract=%b use_faces=%b use_face_step=%b",
430 true,
431 use_faces,
432 use_face_step);
433 BMO_op_exec(em->bm, &bmop);
434 /* Don't flush selection in edge/vertex mode. */
436 em->bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, use_faces ? true : false);
437 BMO_op_finish(em->bm, &bmop);
438
440
441 /* only needed for select less, ensure we don't have isolated elements remaining */
443}
444
445void EDBM_flag_disable_all(BMEditMesh *em, const char hflag)
446{
448}
449
450void EDBM_flag_enable_all(BMEditMesh *em, const char hflag)
451{
453}
454
456
457/* -------------------------------------------------------------------- */
460
461UvVertMap *BM_uv_vert_map_create(BMesh *bm, const bool use_select)
462{
463 /* NOTE: delimiting on alternate face-winding was once supported and could be useful
464 * in some cases. If this is need see: D17137 to restore support. */
465 BMVert *ev;
466 BMFace *efa;
467 BMLoop *l;
468 BMIter iter, liter;
469 uint a;
470 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
471
473
474 const int totverts = bm->totvert;
475 int totuv = 0;
476
477 /* generate UvMapVert array */
478 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
479 if ((use_select == false) || BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
480 totuv += efa->len;
481 }
482 }
483
484 if (totuv == 0) {
485 return nullptr;
486 }
487 UvVertMap *vmap = (UvVertMap *)MEM_callocN(sizeof(*vmap), "UvVertMap");
488 if (!vmap) {
489 return nullptr;
490 }
491
492 vmap->vert = MEM_calloc_arrayN<UvMapVert *>(totverts, "UvMapVert_pt");
493 UvMapVert *buf = vmap->buf = MEM_calloc_arrayN<UvMapVert>(totuv, "UvMapVert");
494
495 if (!vmap->vert || !vmap->buf) {
497 return nullptr;
498 }
499
500 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, a) {
501 if ((use_select == false) || BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
502 int i;
503 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
504 buf->loop_of_face_index = i;
505 buf->face_index = a;
506 buf->separate = false;
507
508 buf->next = vmap->vert[BM_elem_index_get(l->v)];
509 vmap->vert[BM_elem_index_get(l->v)] = buf;
510 buf++;
511 }
512 }
513 }
514
515 /* sort individual uvs for each vert */
516 BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, a) {
517 UvMapVert *newvlist = nullptr, *vlist = vmap->vert[a];
518 UvMapVert *iterv, *v, *lastv, *next;
519 const float *uv, *uv2;
520
521 while (vlist) {
522 v = vlist;
523 vlist = vlist->next;
524 v->next = newvlist;
525 newvlist = v;
526
527 efa = BM_face_at_index(bm, v->face_index);
528
529 l = static_cast<BMLoop *>(
530 BM_iter_at_index(bm, BM_LOOPS_OF_FACE, efa, v->loop_of_face_index));
531 uv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
532
533 lastv = nullptr;
534 iterv = vlist;
535
536 while (iterv) {
537 next = iterv->next;
538 efa = BM_face_at_index(bm, iterv->face_index);
539 l = static_cast<BMLoop *>(
541 uv2 = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
542
543 if (compare_v2v2(uv2, uv, STD_UV_CONNECT_LIMIT)) {
544 if (lastv) {
545 lastv->next = next;
546 }
547 else {
548 vlist = next;
549 }
550 iterv->next = newvlist;
551 newvlist = iterv;
552 }
553 else {
554 lastv = iterv;
555 }
556
557 iterv = next;
558 }
559
560 newvlist->separate = true;
561 }
562
563 vmap->vert[a] = newvlist;
564 }
565
566 return vmap;
567}
568
570{
571 return vmap->vert[v];
572}
573
575{
576 if (element_map->head_table) {
577 return element_map->head_table;
578 }
579
580 /* For each UvElement, locate the "separate" UvElement that precedes it in the linked list. */
581 element_map->head_table = static_cast<UvElement **>(
582 MEM_mallocN(sizeof(*element_map->head_table) * element_map->total_uvs, __func__));
583 UvElement **head_table = element_map->head_table;
584 for (int i = 0; i < element_map->total_uvs; i++) {
585 UvElement *head = element_map->storage + i;
586 if (head->separate) {
587 UvElement *element = head;
588 while (element) {
589 head_table[element - element_map->storage] = head;
590 element = element->next;
591 if (element && element->separate) {
592 break;
593 }
594 }
595 }
596 }
597 return element_map->head_table;
598}
599
601{
602 if (!element_map->unique_index_table) {
603 element_map->unique_index_table = static_cast<int *>(
604 MEM_callocN(element_map->total_uvs * sizeof(*element_map->unique_index_table), __func__));
605
606 int j = 0;
607 for (int i = 0; i < element_map->total_uvs; i++) {
608 UvElement *element = element_map->storage + i;
609 if (!element->separate) {
610 continue;
611 }
612 BLI_assert(0 <= j);
613 BLI_assert(j < element_map->total_unique_uvs);
614 while (element) {
615 element_map->unique_index_table[element - element_map->storage] = j;
616 element = element->next;
617 if (!element || element->separate) {
618 break;
619 }
620 }
621 j++;
622 }
623 BLI_assert(j == element_map->total_unique_uvs);
624 }
625
626 return element_map->unique_index_table;
627}
628
630{
631 int *unique_index = BM_uv_element_map_ensure_unique_index(element_map);
632 int index = child - element_map->storage;
633 BLI_assert(0 <= index);
634 BLI_assert(index < element_map->total_uvs);
635 return unique_index[index];
636}
637
638#define INVALID_ISLAND uint(-1)
639
640static void bm_uv_assign_island(UvElementMap *element_map,
642 int nisland,
643 uint *map,
644 UvElement *islandbuf,
645 int islandbufsize)
646{
647 element->island = nisland;
648 map[element - element_map->storage] = islandbufsize;
649
650 /* Copy *element to islandbuf[islandbufsize]. */
651 islandbuf[islandbufsize].l = element->l;
652 islandbuf[islandbufsize].separate = element->separate;
653 islandbuf[islandbufsize].loop_of_face_index = element->loop_of_face_index;
654 islandbuf[islandbufsize].island = element->island;
655 islandbuf[islandbufsize].flag = element->flag;
656}
657
659 const Scene *scene,
660 UvElement *islandbuf,
661 uint *map,
662 bool uv_selected,
663 const BMUVOffsets &offsets)
664{
666
667 int total_uvs = element_map->total_uvs;
668
669 /* Depth first search the graph, building islands as we go. */
670 int nislands = 0;
671 int islandbufsize = 0;
672 int stack_upper_bound = total_uvs;
673 UvElement **stack_uv = static_cast<UvElement **>(
674 MEM_mallocN(sizeof(*stack_uv) * stack_upper_bound, __func__));
675 int stacksize_uv = 0;
676 for (int i = 0; i < total_uvs; i++) {
677 UvElement *element = element_map->storage + i;
678 if (element->island != INVALID_ISLAND) {
679 /* Unique UV (element and all its children) are already part of an island. */
680 continue;
681 }
682
683 /* Create a new island, i.e. nislands++. */
684
685 BLI_assert(element->separate); /* Ensure we're the head of this unique UV. */
686
687 /* Seed the graph search. */
688 stack_uv[stacksize_uv++] = element;
689 while (element) {
690 bm_uv_assign_island(element_map, element, nislands, map, islandbuf, islandbufsize++);
691 element = element->next;
692 if (element && element->separate) {
693 break;
694 }
695 }
696
697 /* Traverse the graph. */
698 while (stacksize_uv) {
699 BLI_assert(stacksize_uv < stack_upper_bound);
700 element = stack_uv[--stacksize_uv];
701 while (element) {
702
703 /* Scan forwards around the BMFace that contains element->l. */
704 if (!uv_selected || uvedit_edge_select_test(scene, element->l, offsets)) {
705 UvElement *next = BM_uv_element_get(element_map, element->l->next);
706 if (next && next->island == INVALID_ISLAND) {
707 UvElement *tail = element_map->head_table[next - element_map->storage];
708 stack_uv[stacksize_uv++] = tail;
709 while (tail) {
710 bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++);
711 tail = tail->next;
712 if (tail && tail->separate) {
713 break;
714 }
715 }
716 }
717 }
718
719 /* Scan backwards around the BMFace that contains element->l. */
720 if (!uv_selected || uvedit_edge_select_test(scene, element->l->prev, offsets)) {
721 UvElement *prev = BM_uv_element_get(element_map, element->l->prev);
722 if (prev && prev->island == INVALID_ISLAND) {
723 UvElement *tail = element_map->head_table[prev - element_map->storage];
724 stack_uv[stacksize_uv++] = tail;
725 while (tail) {
726 bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++);
727 tail = tail->next;
728 if (tail && tail->separate) {
729 break;
730 }
731 }
732 }
733 }
734
735 /* The same for all the UvElements in this unique UV. */
736 element = element->next;
737 if (element && element->separate) {
738 break;
739 }
740 }
741 }
742 nislands++;
743 }
744 BLI_assert(islandbufsize == total_uvs);
745
746 MEM_SAFE_FREE(stack_uv);
747 MEM_SAFE_FREE(element_map->head_table);
748
749 return nislands;
750}
751
752static void bm_uv_build_islands(UvElementMap *element_map,
753 BMesh *bm,
754 const Scene *scene,
755 bool uv_selected)
756{
757 int totuv = element_map->total_uvs;
758 int nislands = 0;
759 int islandbufsize = 0;
760
761 /* map holds the map from current vmap->buf to the new, sorted map */
762 uint *map = MEM_malloc_arrayN<uint>(totuv, __func__);
763 BMFace **stack = MEM_malloc_arrayN<BMFace *>(bm->totface, __func__);
764 UvElement *islandbuf = MEM_calloc_arrayN<UvElement>(totuv, __func__);
765 /* Island number for BMFaces. */
766 int *island_number = MEM_calloc_arrayN<int>(bm->totface, __func__);
767 copy_vn_i(island_number, bm->totface, INVALID_ISLAND);
768
769 const BMUVOffsets uv_offsets = BM_uv_map_offsets_get(bm);
770
771 const bool use_uv_edge_connectivity = scene->toolsettings->uv_flag & UV_SYNC_SELECTION ?
774 if (use_uv_edge_connectivity) {
776 element_map, scene, islandbuf, map, uv_selected, uv_offsets);
777 islandbufsize = totuv;
778 }
779
780 for (int i = 0; i < totuv; i++) {
781 if (element_map->storage[i].island == INVALID_ISLAND) {
782 int stacksize = 0;
783 element_map->storage[i].island = nislands;
784 stack[0] = element_map->storage[i].l->f;
785 island_number[BM_elem_index_get(stack[0])] = nislands;
786 stacksize = 1;
787
788 while (stacksize > 0) {
789 BMFace *efa = stack[--stacksize];
790
791 BMLoop *l;
792 BMIter liter;
793 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
794 if (uv_selected && !uvedit_uv_select_test(scene, l, uv_offsets)) {
795 continue;
796 }
797
798 UvElement *initelement = element_map->vertex[BM_elem_index_get(l->v)];
799
800 for (UvElement *element = initelement; element; element = element->next) {
801 if (element->separate) {
802 initelement = element;
803 }
804
805 if (element->l->f == efa) {
806 /* found the uv corresponding to our face and vertex.
807 * Now fill it to the buffer */
808 bm_uv_assign_island(element_map, element, nislands, map, islandbuf, islandbufsize++);
809
810 for (element = initelement; element; element = element->next) {
811 if (element->separate && element != initelement) {
812 break;
813 }
814
815 if (island_number[BM_elem_index_get(element->l->f)] == INVALID_ISLAND) {
816 stack[stacksize++] = element->l->f;
817 island_number[BM_elem_index_get(element->l->f)] = nislands;
818 }
819 }
820 break;
821 }
822 }
823 }
824 }
825
826 nislands++;
827 }
828 }
829
830 MEM_SAFE_FREE(island_number);
831
832 /* remap */
833 for (int i = 0; i < bm->totvert; i++) {
834 /* important since we may do selection only. Some of these may be nullptr */
835 if (element_map->vertex[i]) {
836 element_map->vertex[i] = &islandbuf[map[element_map->vertex[i] - element_map->storage]];
837 }
838 }
839
840 element_map->island_indices = MEM_calloc_arrayN<int>(nislands, __func__);
841 element_map->island_total_uvs = MEM_calloc_arrayN<int>(nislands, __func__);
842 element_map->island_total_unique_uvs = MEM_calloc_arrayN<int>(nislands, __func__);
843 int j = 0;
844 for (int i = 0; i < totuv; i++) {
845 UvElement *next = element_map->storage[i].next;
846 islandbuf[map[i]].next = next ? &islandbuf[map[next - element_map->storage]] : nullptr;
847
848 if (islandbuf[i].island != j) {
849 j++;
850 element_map->island_indices[j] = i;
851 }
852 BLI_assert(islandbuf[i].island == j);
853 element_map->island_total_uvs[j]++;
854 if (islandbuf[i].separate) {
855 element_map->island_total_unique_uvs[j]++;
856 }
857 }
858
859 MEM_SAFE_FREE(element_map->storage);
860 element_map->storage = islandbuf;
861 islandbuf = nullptr;
862 element_map->total_islands = nislands;
863 MEM_SAFE_FREE(stack);
864 MEM_SAFE_FREE(map);
865}
866
868static bool loop_uv_match(BMLoop *loop,
869 const float luv_a[2],
870 const float luv_b[2],
871 int cd_loop_uv_offset)
872{
873 const float *luv_c = BM_ELEM_CD_GET_FLOAT_P(loop, cd_loop_uv_offset);
874 const float *luv_d = BM_ELEM_CD_GET_FLOAT_P(loop->next, cd_loop_uv_offset);
875 return compare_v2v2(luv_a, luv_c, STD_UV_CONNECT_LIMIT) &&
876 compare_v2v2(luv_b, luv_d, STD_UV_CONNECT_LIMIT);
877}
878
898 const float luv_anchor[2],
899 const float luv_fan[2],
900 const BMLoop *needle,
901 GSet *visited,
902 const int cd_loop_uv_offset)
903{
904 BMVert *anchor = needle->v;
905 BLI_assert(edge->v1 == anchor || edge->v2 == anchor);
906
907 if (BM_elem_flag_test(edge, BM_ELEM_SEAM)) {
908 return false; /* Edge is a seam, don't traverse. */
909 }
910
911 if (!BLI_gset_add(visited, edge)) {
912 return false; /* Already visited. */
913 }
914
915 BMLoop *loop;
916 BMIter liter;
917 BM_ITER_ELEM (loop, &liter, edge, BM_LOOPS_OF_EDGE) {
918 if (loop->v == anchor) {
919 if (!loop_uv_match(loop, luv_anchor, luv_fan, cd_loop_uv_offset)) {
920 continue; /* `loop` is disjoint in UV space. */
921 }
922
923 if (loop == needle) {
924 return true; /* Success. */
925 }
926
927 const float *luv_far = BM_ELEM_CD_GET_FLOAT_P(loop->prev, cd_loop_uv_offset);
929 loop->prev->e, luv_anchor, luv_far, needle, visited, cd_loop_uv_offset))
930 {
931 return true;
932 }
933 }
934 else {
935 BLI_assert(loop->next->v == anchor);
936 if (!loop_uv_match(loop, luv_fan, luv_anchor, cd_loop_uv_offset)) {
937 continue; /* `loop` is disjoint in UV space. */
938 }
939
940 if (loop->next == needle) {
941 return true; /* Success. */
942 }
943
944 const float *luv_far = BM_ELEM_CD_GET_FLOAT_P(loop->next->next, cd_loop_uv_offset);
946 loop->next->e, luv_anchor, luv_far, needle, visited, cd_loop_uv_offset))
947 {
948 return true;
949 }
950 }
951 }
952
953 return false;
954}
955
962static bool seam_connected(BMLoop *loop_a, BMLoop *loop_b, GSet *visited, int cd_loop_uv_offset)
963{
964 BLI_assert(loop_a && loop_b);
965 BLI_assert(loop_a != loop_b);
966 BLI_assert(loop_a->v == loop_b->v);
967
968 BLI_gset_clear(visited, nullptr);
969
970 const float *luv_anchor = BM_ELEM_CD_GET_FLOAT_P(loop_a, cd_loop_uv_offset);
971 const float *luv_next_fan = BM_ELEM_CD_GET_FLOAT_P(loop_a->next, cd_loop_uv_offset);
973 loop_a->e, luv_anchor, luv_next_fan, loop_b, visited, cd_loop_uv_offset);
974 if (!result) {
975 /* Search around `loop_a` in the opposite direction, as one of the edges may be delimited by
976 * a boundary, seam or disjoint UV, or itself be one of these. See: #103670, #103787. */
977 const float *luv_prev_fan = BM_ELEM_CD_GET_FLOAT_P(loop_a->prev, cd_loop_uv_offset);
979 loop_a->prev->e, luv_anchor, luv_prev_fan, loop_b, visited, cd_loop_uv_offset);
980 }
981
982 return result;
983}
984
986 const Scene *scene,
987 const bool uv_selected,
988 const bool use_winding,
989 const bool use_seams,
990 const bool do_islands)
991{
992 /* In uv sync selection, all UVs (from unhidden geometry) are visible. */
993 const bool face_selected = !(scene->toolsettings->uv_flag & UV_SYNC_SELECTION);
994
995 BMVert *ev;
996 BMFace *efa;
997 BMIter iter, liter;
998
999 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
1000 if (offsets.uv < 0) {
1001 return nullptr;
1002 }
1003
1005
1006 /* Count total uvs. */
1007 int totuv = 0;
1008 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1010 continue;
1011 }
1012
1013 if (face_selected && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1014 continue;
1015 }
1016
1017 if (!uv_selected) {
1018 totuv += efa->len;
1019 }
1020 else {
1021 BMLoop *l;
1022 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1023 if (uvedit_uv_select_test(scene, l, offsets)) {
1024 totuv++;
1025 }
1026 }
1027 }
1028 }
1029
1030 if (totuv == 0) {
1031 return nullptr;
1032 }
1033
1034 UvElementMap *element_map = (UvElementMap *)MEM_callocN(sizeof(*element_map), "UvElementMap");
1035 element_map->total_uvs = totuv;
1036 element_map->vertex = (UvElement **)MEM_callocN(sizeof(*element_map->vertex) * bm->totvert,
1037 "UvElementVerts");
1038 element_map->storage = (UvElement *)MEM_callocN(sizeof(*element_map->storage) * totuv,
1039 "UvElement");
1040
1041 bool *winding = use_winding ? MEM_calloc_arrayN<bool>(bm->totface, "winding") : nullptr;
1042
1043 UvElement *buf = element_map->storage;
1044 int j;
1045 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, j) {
1046
1048 continue;
1049 }
1050
1051 if (face_selected && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1052 continue;
1053 }
1054
1055 int i;
1056 BMLoop *l;
1057 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
1058 if (uv_selected && !uvedit_uv_select_test(scene, l, offsets)) {
1059 continue;
1060 }
1061
1062 buf->l = l;
1063 buf->island = INVALID_ISLAND;
1064 buf->loop_of_face_index = i;
1065
1066 /* Insert to head of linked list associated with BMVert. */
1067 buf->next = element_map->vertex[BM_elem_index_get(l->v)];
1068 element_map->vertex[BM_elem_index_get(l->v)] = buf;
1069
1070 buf++;
1071 }
1072
1073 if (winding) {
1074 winding[j] = BM_face_calc_area_uv_signed(efa, offsets.uv) > 0;
1075 }
1076 }
1077
1078 GSet *seam_visited_gset = use_seams ? BLI_gset_ptr_new(__func__) : nullptr;
1079
1080 /* For each BMVert, sort associated linked list into unique uvs. */
1081 int ev_index;
1082 BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, ev_index) {
1083 UvElement *newvlist = nullptr;
1084 UvElement *vlist = element_map->vertex[ev_index];
1085 while (vlist) {
1086
1087 /* Detach head from unsorted list. */
1088 UvElement *v = vlist;
1089 vlist = vlist->next;
1090 v->next = newvlist;
1091 newvlist = v;
1092
1093 const float *uv = static_cast<const float *>(BM_ELEM_CD_GET_VOID_P(v->l, offsets.uv));
1094 bool uv_vert_sel = uvedit_uv_select_test(scene, v->l, offsets);
1095
1096 UvElement *lastv = nullptr;
1097 UvElement *iterv = vlist;
1098
1099 /* Scan through unsorted list, finding UvElements which are connected to `v`. */
1100 while (iterv) {
1101 UvElement *next = iterv->next;
1102
1103 bool connected = true; /* Assume connected unless we can prove otherwise. */
1104
1105 if (connected) {
1106 /* Are the two UVs close together? */
1107 const float *uv2 = BM_ELEM_CD_GET_FLOAT_P(iterv->l, offsets.uv);
1108 connected = compare_v2v2(uv2, uv, STD_UV_CONNECT_LIMIT);
1109 }
1110
1111 if (connected) {
1112 /* Check if the uv loops share the same selection state (if not, they are not connected
1113 * as they have been ripped or other edit commands have separated them). */
1114 const bool uv2_vert_sel = uvedit_uv_select_test(scene, iterv->l, offsets);
1115 connected = (uv_vert_sel == uv2_vert_sel);
1116 }
1117
1118 if (connected && use_winding) {
1119 connected = winding[BM_elem_index_get(iterv->l->f)] ==
1120 winding[BM_elem_index_get(v->l->f)];
1121 }
1122
1123 if (connected && use_seams) {
1124 connected = seam_connected(iterv->l, v->l, seam_visited_gset, offsets.uv);
1125 }
1126
1127 if (connected) {
1128 if (lastv) {
1129 lastv->next = next;
1130 }
1131 else {
1132 vlist = next;
1133 }
1134 iterv->next = newvlist;
1135 newvlist = iterv;
1136 }
1137 else {
1138 lastv = iterv;
1139 }
1140
1141 iterv = next;
1142 }
1143
1144 element_map->total_unique_uvs++;
1145 newvlist->separate = true;
1146 }
1147
1148 /* Write back sorted list. */
1149 element_map->vertex[ev_index] = newvlist;
1150 }
1151
1152 if (seam_visited_gset) {
1153 BLI_gset_free(seam_visited_gset, nullptr);
1154 seam_visited_gset = nullptr;
1155 }
1156 MEM_SAFE_FREE(winding);
1157
1158 /* at this point, every UvElement in vert points to a UvElement sharing the same vertex.
1159 * Now we should sort uv's in islands. */
1160 if (do_islands) {
1161 bm_uv_build_islands(element_map, bm, scene, uv_selected);
1162 }
1163
1164 /* TODO: Confirm element_map->total_unique_uvs doesn't require recalculating. */
1165 element_map->total_unique_uvs = 0;
1166 for (int i = 0; i < element_map->total_uvs; i++) {
1167 if (element_map->storage[i].separate) {
1168 element_map->total_unique_uvs++;
1169 }
1170 }
1171
1172 return element_map;
1173}
1174
1176{
1177 if (vmap) {
1178 if (vmap->vert) {
1179 MEM_freeN(vmap->vert);
1180 }
1181 if (vmap->buf) {
1182 MEM_freeN(vmap->buf);
1183 }
1184 MEM_freeN(vmap);
1185 }
1186}
1187
1189{
1190 if (element_map) {
1191 MEM_SAFE_FREE(element_map->storage);
1192 MEM_SAFE_FREE(element_map->vertex);
1193 MEM_SAFE_FREE(element_map->head_table);
1194 MEM_SAFE_FREE(element_map->unique_index_table);
1195 MEM_SAFE_FREE(element_map->island_indices);
1196 MEM_SAFE_FREE(element_map->island_total_uvs);
1198 MEM_SAFE_FREE(element_map);
1199 }
1200}
1201
1203{
1204 UvElement *element = element_map->vertex[BM_elem_index_get(l->v)];
1205 while (element) {
1206 if (element->l == l) {
1207 return element;
1208 }
1209 element = element->next;
1210 }
1211
1212 return nullptr;
1213}
1214
1216{
1217 if (!child) {
1218 return nullptr;
1219 }
1220
1221 return element_map->vertex[BM_elem_index_get(child->l->v)];
1222}
1223
1225
1226/* -------------------------------------------------------------------- */
1229
1230BMFace *EDBM_uv_active_face_get(BMEditMesh *em, const bool sloppy, const bool selected)
1231{
1232 if (!EDBM_uv_check(em)) {
1233 return nullptr;
1234 }
1235 BMFace *efa = BM_mesh_active_face_get(em->bm, sloppy, selected);
1236 if (efa) {
1237 return efa;
1238 }
1239
1240 return nullptr;
1241}
1242
1244{
1245 /* some of these checks could be a touch overkill */
1246 return em && em->bm->totface && CustomData_has_layer(&em->bm->ldata, CD_PROP_FLOAT2);
1247}
1248
1250{
1251 /* some of these checks could be a touch overkill */
1252 return em && em->bm->totface && CustomData_has_layer(&em->bm->ldata, CD_PROP_BYTE_COLOR);
1253}
1254
1256
1257/* -------------------------------------------------------------------- */
1260
1261static BMVert *cache_mirr_intptr_as_bmvert(const intptr_t *index_lookup, int index)
1262{
1263 intptr_t eve_i = index_lookup[index];
1264 return (eve_i == -1) ? nullptr : (BMVert *)eve_i;
1265}
1266
1282
1283/* BM_SEARCH_MAXDIST is too big, copied from 2.6x MOC_THRESH, should become a preference. */
1284#define BM_SEARCH_MAXDIST_MIRR 0.00002f
1285#define BM_CD_LAYER_ID "__mirror_index"
1286
1288 const int axis,
1289 const bool use_self,
1290 const bool use_select,
1291 const bool respecthide,
1292 /* extra args */
1293 const bool use_topology,
1294 float maxdist,
1295 int *r_index)
1296{
1297 BMesh *bm = em->bm;
1298 BMIter iter;
1299 BMVert *v;
1300 int cd_vmirr_offset = 0;
1301 int i;
1302 const float maxdist_sq = square_f(maxdist);
1303
1304 /* one or the other is used depending if topo is enabled */
1305 KDTree_3d *tree = nullptr;
1306 MirrTopoStore_t mesh_topo_store = {nullptr, -1, -1, false};
1307
1309
1310 if (r_index == nullptr) {
1311 const char *layer_id = BM_CD_LAYER_ID;
1313 if (em->mirror_cdlayer == -1) {
1314 BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_INT32, layer_id);
1316 }
1317
1318 cd_vmirr_offset = CustomData_get_n_offset(
1319 &bm->vdata,
1322
1323 bm->vdata.layers[em->mirror_cdlayer].flag |= CD_FLAG_TEMPORARY;
1324 }
1325
1327
1328 if (use_topology) {
1329 ED_mesh_mirrtopo_init(em, nullptr, &mesh_topo_store, true);
1330 }
1331 else {
1332 tree = BLI_kdtree_3d_new(bm->totvert);
1334 if (respecthide && BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
1335 continue;
1336 }
1337
1338 BLI_kdtree_3d_insert(tree, i, v->co);
1339 }
1340 BLI_kdtree_3d_balance(tree);
1341 }
1342
1343#define VERT_INTPTR(_v, _i) \
1344 (r_index ? &r_index[_i] : static_cast<int *>(BM_ELEM_CD_GET_VOID_P(_v, cd_vmirr_offset)))
1345
1347 if (respecthide && BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
1348 continue;
1349 }
1350
1351 if (use_select && !BM_elem_flag_test(v, BM_ELEM_SELECT)) {
1352 continue;
1353 }
1354
1356 BMVert *v_mirr;
1357 int *idx = VERT_INTPTR(v, i);
1358
1359 if (use_topology) {
1360 v_mirr = cache_mirr_intptr_as_bmvert(mesh_topo_store.index_lookup, i);
1361 if (v_mirr != nullptr) {
1362 if (respecthide && BM_elem_flag_test(v_mirr, BM_ELEM_HIDDEN)) {
1363 v_mirr = nullptr;
1364 }
1365 }
1366 }
1367 else {
1368 int i_mirr;
1369 float co[3];
1370 copy_v3_v3(co, v->co);
1371 co[axis] *= -1.0f;
1372
1373 v_mirr = nullptr;
1374 i_mirr = BLI_kdtree_3d_find_nearest(tree, co, nullptr);
1375 if (i_mirr != -1) {
1376 BMVert *v_test = BM_vert_at_index(bm, i_mirr);
1377 if (len_squared_v3v3(co, v_test->co) < maxdist_sq) {
1378 v_mirr = v_test;
1379 }
1380 }
1381 }
1382
1383 if (v_mirr && (use_self || (v_mirr != v))) {
1384 const int i_mirr = BM_elem_index_get(v_mirr);
1385 *idx = i_mirr;
1386 idx = VERT_INTPTR(v_mirr, i_mirr);
1387 *idx = i;
1388 }
1389 else {
1390 *idx = -1;
1391 }
1392 }
1393
1394#undef VERT_INTPTR
1395
1396 if (use_topology) {
1398 }
1399 else {
1400 BLI_kdtree_3d_free(tree);
1401 }
1402}
1403
1405 const int axis,
1406 const bool use_self,
1407 const bool use_select,
1408 const bool respecthide,
1409 const bool use_topology)
1410{
1412 axis,
1413 use_self,
1414 use_select,
1415 respecthide,
1416 /* extra args */
1417 use_topology,
1419 nullptr);
1420}
1421
1423{
1424 const int *mirr = static_cast<const int *>(
1425 CustomData_bmesh_get_layer_n(&em->bm->vdata, v->head.data, em->mirror_cdlayer));
1426
1427 BLI_assert(em->mirror_cdlayer != -1); /* invalid use */
1428
1429 if (mirr && *mirr >= 0 && *mirr < em->bm->totvert) {
1430 if (!em->bm->vtable) {
1431 printf(
1432 "err: should only be called between "
1433 "EDBM_verts_mirror_cache_begin and EDBM_verts_mirror_cache_end");
1434 return nullptr;
1435 }
1436
1437 return em->bm->vtable[*mirr];
1438 }
1439
1440 return nullptr;
1441}
1442
1444{
1445 BMVert *v1_mirr, *v2_mirr;
1446 if ((v1_mirr = EDBM_verts_mirror_get(em, e->v1)) &&
1447 (v2_mirr = EDBM_verts_mirror_get(em, e->v2)) &&
1448 /* While highly unlikely, a zero length central edges vertices can match, see #89342. */
1449 LIKELY(v1_mirr != v2_mirr))
1450 {
1451 return BM_edge_exists(v1_mirr, v2_mirr);
1452 }
1453
1454 return nullptr;
1455}
1456
1458{
1460
1461 BMLoop *l_iter, *l_first;
1462 uint i = 0;
1463
1464 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1465 do {
1466 if ((v_mirr_arr[i++] = EDBM_verts_mirror_get(em, l_iter->v)) == nullptr) {
1467 return nullptr;
1468 }
1469 } while ((l_iter = l_iter->next) != l_first);
1470
1471 return BM_face_exists(v_mirr_arr.data(), v_mirr_arr.size());
1472}
1473
1475{
1476 int *mirr = static_cast<int *>(
1477 CustomData_bmesh_get_layer_n(&em->bm->vdata, v->head.data, em->mirror_cdlayer));
1478
1479 BLI_assert(em->mirror_cdlayer != -1); /* invalid use */
1480
1481 if (mirr) {
1482 *mirr = -1;
1483 }
1484}
1485
1487{
1488 em->mirror_cdlayer = -1;
1489}
1490
1491void EDBM_verts_mirror_apply(BMEditMesh *em, const int sel_from, const int sel_to)
1492{
1493 BMIter iter;
1494 BMVert *v;
1495
1496 BLI_assert((em->bm->vtable != nullptr) && ((em->bm->elem_table_dirty & BM_VERT) == 0));
1497
1498 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
1499 if (BM_elem_flag_test(v, BM_ELEM_SELECT) == sel_from) {
1500 BMVert *mirr = EDBM_verts_mirror_get(em, v);
1501 if (mirr) {
1502 if (BM_elem_flag_test(mirr, BM_ELEM_SELECT) == sel_to) {
1503 copy_v3_v3(mirr->co, v->co);
1504 mirr->co[0] *= -1.0f;
1505 }
1506 }
1507 }
1508 }
1509}
1510
1512
1513/* -------------------------------------------------------------------- */
1516
1518{
1519 BMIter iter;
1520 BMElem *ele;
1521 int itermode;
1522 char hflag_swap = swap ? BM_ELEM_SELECT : 0;
1523 bool changed = true;
1524
1525 if (em->selectmode & SCE_SELECT_VERTEX) {
1526 itermode = BM_VERTS_OF_MESH;
1527 }
1528 else if (em->selectmode & SCE_SELECT_EDGE) {
1529 itermode = BM_EDGES_OF_MESH;
1530 }
1531 else {
1532 itermode = BM_FACES_OF_MESH;
1533 }
1534
1535 BM_ITER_MESH (ele, &iter, em->bm, itermode) {
1536 if (!BM_elem_flag_test(ele, BM_ELEM_HIDDEN)) {
1537 if (BM_elem_flag_test(ele, BM_ELEM_SELECT) ^ hflag_swap) {
1538 BM_elem_hide_set(em->bm, ele, true);
1539 changed = true;
1540 }
1541 }
1542 }
1543
1544 if (changed) {
1546 }
1547 return changed;
1548
1549 /* original hide flushing comment (OUTDATED):
1550 * hide happens on least dominant select mode, and flushes up, not down!
1551 * (helps preventing errors in subsurf) */
1552 /* - vertex hidden, always means edge is hidden too
1553 * - edge hidden, always means face is hidden too
1554 * - face hidden, only set face hide
1555 * - then only flush back down what's absolute hidden
1556 */
1557}
1558
1560{
1561 const char iter_types[3] = {
1565 };
1566
1567 const bool sels[3] = {
1568 (em->selectmode & SCE_SELECT_VERTEX) != 0,
1569 (em->selectmode & SCE_SELECT_EDGE) != 0,
1570 (em->selectmode & SCE_SELECT_FACE) != 0,
1571 };
1572 int i;
1573 bool changed = false;
1574
1575 /* Use tag flag to remember what was hidden before all is revealed.
1576 * BM_ELEM_HIDDEN --> BM_ELEM_TAG */
1577 for (i = 0; i < 3; i++) {
1578 BMIter iter;
1579 BMElem *ele;
1580
1581 BM_ITER_MESH (ele, &iter, em->bm, iter_types[i]) {
1584 changed = true;
1585 }
1586 else {
1588 }
1589 }
1590 }
1591
1592 if (!changed) {
1593 return false;
1594 }
1595
1596 /* Reveal everything */
1598
1599 /* Select relevant just-revealed elements */
1600 for (i = 0; i < 3; i++) {
1601 BMIter iter;
1602 BMElem *ele;
1603
1604 if (!sels[i]) {
1605 continue;
1606 }
1607
1608 BM_ITER_MESH (ele, &iter, em->bm, iter_types[i]) {
1609 if (BM_elem_flag_test(ele, BM_ELEM_TAG)) {
1610 BM_elem_select_set(em->bm, ele, select);
1611 }
1612 }
1613 }
1614
1616
1617 /* hidden faces can have invalid normals */
1619
1620 return true;
1621}
1622
1624
1625/* -------------------------------------------------------------------- */
1628
1633
1635{
1637 params.face_normals = true;
1639}
1640
1642{
1643 const char iter_types[3] = {
1647 };
1648
1649 BMIter iter;
1650 BMElem *ele;
1651 int *tots[3];
1652 int i;
1653
1654 tots[0] = &em->bm->totvertsel;
1655 tots[1] = &em->bm->totedgesel;
1656 tots[2] = &em->bm->totfacesel;
1657
1658 em->bm->totvertsel = em->bm->totedgesel = em->bm->totfacesel = 0;
1659
1660 for (i = 0; i < 3; i++) {
1661 ele = static_cast<BMElem *>(BM_iter_new(&iter, em->bm, iter_types[i], nullptr));
1662 for (; ele; ele = static_cast<BMElem *>(BM_iter_step(&iter))) {
1664 (*tots[i])++;
1665 }
1666 }
1667 }
1668}
1669
1671{
1672 BMEditMesh *em = mesh->runtime->edit_mesh.get();
1673 /* Order of calling isn't important. */
1676
1677 if (params->calc_normals && params->calc_looptris) {
1678 /* Calculating both has some performance gains. */
1680 }
1681 else {
1682 if (params->calc_normals) {
1684 }
1685
1686 if (params->calc_looptris) {
1688 }
1689 }
1690
1691 if (params->is_destructive) {
1692 /* TODO(@ideasman42): we may be able to remove this now! */
1693 // BM_mesh_elem_table_free(em->bm, BM_ALL_NOLOOP);
1694 }
1695 else {
1696 /* in debug mode double check we didn't need to recalculate */
1698 }
1700 BM_lnorspace_invalidate(em->bm, false);
1702 }
1703
1704#ifndef NDEBUG
1705 {
1708 }
1709 }
1710#endif
1711}
1712
1713void EDBM_update_extern(Mesh *mesh, const bool do_tessellation, const bool is_destructive)
1714{
1716 params.calc_looptris = do_tessellation;
1717 params.calc_normals = false;
1718 params.is_destructive = is_destructive;
1719 EDBM_update(mesh, &params);
1720}
1721
1723
1724/* -------------------------------------------------------------------- */
1727
1729{
1731 return true;
1732 }
1733
1734 return false;
1735}
1736
1738
1739/* -------------------------------------------------------------------- */
1742
1744{
1745 if ((em->selectmode & SCE_SELECT_VERTEX) && eve) {
1746 return (BMElem *)eve;
1747 }
1748 if ((em->selectmode & SCE_SELECT_EDGE) && eed) {
1749 return (BMElem *)eed;
1750 }
1751 if ((em->selectmode & SCE_SELECT_FACE) && efa) {
1752 return (BMElem *)efa;
1753 }
1754 return nullptr;
1755}
1756
1758{
1759 BMesh *bm = em->bm;
1760 int index = BM_elem_index_get(ele);
1761
1762 if (ele->head.htype == BM_VERT) {
1763 BLI_assert(!(bm->elem_index_dirty & BM_VERT));
1764 }
1765 else if (ele->head.htype == BM_EDGE) {
1766 BLI_assert(!(bm->elem_index_dirty & BM_EDGE));
1767 index += bm->totvert;
1768 }
1769 else if (ele->head.htype == BM_FACE) {
1770 BLI_assert(!(bm->elem_index_dirty & BM_FACE));
1771 index += bm->totvert + bm->totedge;
1772 }
1773 else {
1774 BLI_assert(0);
1775 }
1776
1777 return index;
1778}
1779
1781{
1782 BMesh *bm = em->bm;
1783
1784 if (index < bm->totvert) {
1785 return (BMElem *)BM_vert_at_index_find_or_table(bm, index);
1786 }
1787 index -= bm->totvert;
1788 if (index < bm->totedge) {
1789 return (BMElem *)BM_edge_at_index_find_or_table(bm, index);
1790 }
1791 index -= bm->totedge;
1792 if (index < bm->totface) {
1793 return (BMElem *)BM_face_at_index_find_or_table(bm, index);
1794 }
1795
1796 return nullptr;
1797}
1798
1800 const Scene *scene, ViewLayer *view_layer, BMEditMesh *em, BMElem *ele, int *r_object_index)
1801{
1802 int elem_index = -1;
1803 *r_object_index = -1;
1804 Vector<Base *> bases = BKE_view_layer_array_from_bases_in_edit_mode(scene, view_layer, nullptr);
1805 for (const int base_index : bases.index_range()) {
1806 Base *base_iter = bases[base_index];
1807 if (BKE_editmesh_from_object(base_iter->object) == em) {
1808 *r_object_index = base_index;
1809 elem_index = EDBM_elem_to_index_any(em, ele);
1810 break;
1811 }
1812 }
1813 return elem_index;
1814}
1815
1817 ViewLayer *view_layer,
1818 uint object_index,
1819 uint elem_index,
1820 Object **r_obedit)
1821{
1822 Vector<Base *> bases = BKE_view_layer_array_from_bases_in_edit_mode(scene, view_layer, nullptr);
1823 *r_obedit = nullptr;
1824 Object *obedit = (object_index < bases.size()) ? bases[object_index]->object : nullptr;
1825 if (obedit != nullptr) {
1827 BMElem *ele = EDBM_elem_from_index_any(em, elem_index);
1828 if (ele != nullptr) {
1829 *r_obedit = obedit;
1830 return ele;
1831 }
1832 }
1833 return nullptr;
1834}
1835
1837
1838/* -------------------------------------------------------------------- */
1841
1843 const BMBVHTree *tree, const float co[3], const float dir[3], float *r_hitout, const BMEdge *e)
1844{
1845 BMFace *f = BKE_bmbvh_ray_cast(tree, co, dir, 0.0f, nullptr, r_hitout, nullptr);
1846
1847 if (f && BM_edge_in_face(e, f)) {
1848 return nullptr;
1849 }
1850
1851 return f;
1852}
1853
1854static void scale_point(float c1[3], const float p[3], const float s)
1855{
1856 sub_v3_v3(c1, p);
1857 mul_v3_fl(c1, s);
1858 add_v3_v3(c1, p);
1859}
1860
1862 const BMEdge *e,
1863 const Depsgraph *depsgraph,
1864 const ARegion *region,
1865 const View3D *v3d,
1866 const Object *obedit)
1867{
1868 BMFace *f;
1869 float co1[3], co2[3], co3[3], dir1[3], dir2[3], dir3[3];
1870 float origin[3], invmat[4][4];
1871 float epsilon = 0.01f;
1872 float end[3];
1873 const float mval_f[2] = {
1874 region->winx / 2.0f,
1875 region->winy / 2.0f,
1876 };
1877
1878 ED_view3d_win_to_segment_clipped(depsgraph, region, v3d, mval_f, origin, end, false);
1879
1880 invert_m4_m4(invmat, obedit->object_to_world().ptr());
1881 mul_m4_v3(invmat, origin);
1882
1883 copy_v3_v3(co1, e->v1->co);
1884 mid_v3_v3v3(co2, e->v1->co, e->v2->co);
1885 copy_v3_v3(co3, e->v2->co);
1886
1887 scale_point(co1, co2, 0.99);
1888 scale_point(co3, co2, 0.99);
1889
1890 /* OK, idea is to generate rays going from the camera origin to the
1891 * three points on the edge (v1, mid, v2). */
1892 sub_v3_v3v3(dir1, origin, co1);
1893 sub_v3_v3v3(dir2, origin, co2);
1894 sub_v3_v3v3(dir3, origin, co3);
1895
1896 normalize_v3_length(dir1, epsilon);
1897 normalize_v3_length(dir2, epsilon);
1898 normalize_v3_length(dir3, epsilon);
1899
1900 /* Offset coordinates slightly along view vectors,
1901 * to avoid hitting the faces that own the edge. */
1902 add_v3_v3v3(co1, co1, dir1);
1903 add_v3_v3v3(co2, co2, dir2);
1904 add_v3_v3v3(co3, co3, dir3);
1905
1906 normalize_v3(dir1);
1907 normalize_v3(dir2);
1908 normalize_v3(dir3);
1909
1910 /* do three samplings: left, middle, right */
1911 f = edge_ray_cast(tree, co1, dir1, nullptr, e);
1912 if (f && !edge_ray_cast(tree, co2, dir2, nullptr, e)) {
1913 return true;
1914 }
1915 if (f && !edge_ray_cast(tree, co3, dir3, nullptr, e)) {
1916 return true;
1917 }
1918 if (!f) {
1919 return true;
1920 }
1921
1922 return false;
1923}
1924
1926
1927/* -------------------------------------------------------------------- */
1930
1932 bContext *C, Depsgraph *depsgraph, ARegion *region, Object *obedit, BMEditMesh *em)
1933{
1934 using namespace blender::ed;
1935 BMIter iter;
1936 BMVert *eve;
1937
1938 ED_view3d_init_mats_rv3d(obedit, static_cast<RegionView3D *>(region->regiondata));
1939
1940 Scene *scene = CTX_data_scene(C);
1942
1944 const int snap_flag = scene->toolsettings->snap_flag;
1945
1947 target_op, !(snap_flag & SCE_SNAP_TO_INCLUDE_EDITED), SCE_SNAP_TARGET_NOT_EDITED);
1952
1953 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
1955 float mval[2], co_proj[3];
1956 if (ED_view3d_project_float_object(region, eve->co, mval, V3D_PROJ_TEST_NOP) ==
1958 {
1960 params.snap_target_select = target_op;
1961 params.edit_mode_type = transform ::SNAP_GEOM_FINAL;
1962 params.occlusion_test = transform ::SNAP_OCCLUSION_AS_SEEM;
1964 depsgraph,
1965 region,
1968 &params,
1969 nullptr,
1970 mval,
1971 nullptr,
1972 nullptr,
1973 co_proj,
1974 nullptr))
1975 {
1976 mul_v3_m4v3(eve->co, obedit->world_to_object().ptr(), co_proj);
1977 }
1978 }
1979 }
1980 }
1981
1983}
1984
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
CustomData interface, see also DNA_customdata_types.h.
int CustomData_get_offset(const CustomData *data, eCustomDataType type)
int CustomData_get_n_offset(const CustomData *data, eCustomDataType type, int n)
int CustomData_get_named_layer_index(const CustomData *data, eCustomDataType type, blender::StringRef name)
int CustomData_get_layer_index(const CustomData *data, eCustomDataType type)
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
void * CustomData_bmesh_get_layer_n(const CustomData *data, void *block, int n)
void BKE_editmesh_free_data(BMEditMesh *em)
Definition editmesh.cc:132
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:61
void BKE_editmesh_looptris_calc(BMEditMesh *em)
Definition editmesh.cc:88
void BKE_editmesh_looptris_and_normals_calc(BMEditMesh *em)
Definition editmesh.cc:95
struct BMFace * BKE_bmbvh_ray_cast(const BMBVHTree *tree, const float co[3], const float dir[3], float radius, float *r_dist, float r_hitout[3], float r_cagehit[3])
blender::Vector< Base * > BKE_view_layer_array_from_bases_in_edit_mode(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
BMesh * BKE_mesh_to_bmesh(Mesh *mesh, int active_shapekey, bool add_key_index, const BMeshCreateParams *params)
void BKE_mesh_uv_vert_map_free(UvVertMap *vmap)
#define STD_UV_CONNECT_LIMIT
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
#define BLI_assert(a)
Definition BLI_assert.h:46
struct GSet GSet
Definition BLI_ghash.h:337
GSet * BLI_gset_ptr_new(const char *info)
void BLI_gset_clear(GSet *gs, GSetKeyFreeFP keyfreefp)
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)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
MINLINE float square_f(float a)
void mul_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])
MINLINE void sub_v3_v3(float r[3], const float a[3])
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 mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE bool compare_v2v2(const float v1[2], const float v2[2], float limit) ATTR_WARN_UNUSED_RESULT
void copy_vn_i(int *array_tar, int size, int val)
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3_length(float n[3], float unit_length)
MINLINE float normalize_v3(float n[3])
unsigned int uint
#define SET_FLAG_FROM_TEST(value, test, flag)
#define UNLIKELY(x)
#define LIKELY(x)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
@ CD_PROP_BYTE_COLOR
@ CD_PROP_INT32
@ CD_PROP_FLOAT2
@ CD_FLAG_TEMPORARY
Object is a sort of wrapper for general info.
@ UV_SYNC_SELECTION
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
@ SCE_SNAP_TO_INCLUDE_EDITED
@ SCE_SNAP_TO_INCLUDE_NONEDITED
@ SCE_SNAP_TO_ONLY_SELECTABLE
@ UV_SELECT_EDGE
eSnapTargetOP
@ SCE_SNAP_TARGET_NOT_ACTIVE
@ SCE_SNAP_TARGET_NOT_NONEDITED
@ SCE_SNAP_TARGET_ONLY_SELECTABLE
@ SCE_SNAP_TARGET_NOT_EDITED
@ SCE_SNAP_TO_FACE
BMVert * EDBM_verts_mirror_get(BMEditMesh *em, BMVert *v)
void ED_mesh_mirror_topo_table_end(Object *ob)
Definition meshtools.cc:867
void ED_mesh_mirrtopo_free(MirrTopoStore_t *mesh_topo_store)
void ED_mesh_mirror_spatial_table_end(Object *ob)
void ED_mesh_mirrtopo_init(BMEditMesh *em, Mesh *mesh, MirrTopoStore_t *mesh_topo_store, bool skip_em_vert_array_init)
bool ED_operator_view3d_active(bContext *C)
bool ED_operator_editmesh(bContext *C)
bool uvedit_uv_select_test(const Scene *scene, const BMLoop *l, const BMUVOffsets &offsets)
bool uvedit_edge_select_test(const Scene *scene, const BMLoop *l, const BMUVOffsets &offsets)
@ V3D_PROJ_TEST_NOP
Definition ED_view3d.hh:279
eV3DProjStatus ED_view3d_project_float_object(const ARegion *region, const float co[3], float r_co[2], eV3DProjTest flag)
void ED_view3d_init_mats_rv3d(const Object *ob, RegionView3D *rv3d)
@ V3D_PROJ_RET_OK
Definition ED_view3d.hh:256
bool ED_view3d_win_to_segment_clipped(const Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const float mval[2], float r_ray_start[3], float r_ray_end[3], bool do_clip_planes)
Read Guarded memory(de)allocation.
static void free_data(ModifierData *md)
Definition MOD_bevel.cc:271
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:390
#define ND_DATA
Definition WM_types.hh:506
#define NC_SCENE
Definition WM_types.hh:375
#define ND_TOOLSETTINGS
Definition WM_types.hh:446
@ BM_ELEM_HIDDEN
@ BM_ELEM_SEAM
@ BM_ELEM_SELECT
@ BM_ELEM_TAG
#define BM_ALL_NOLOOP
@ BM_SPACEARR_BMO_SET
#define BM_FACE_FIRST_LOOP(p)
#define BM_ELEM_CD_GET_FLOAT_P(ele, offset)
#define BM_ELEM_CD_GET_VOID_P(ele, offset)
BMesh * BM_mesh_copy(BMesh *bm_old)
eBMOpErrorLevel
@ BMO_ERROR_WARN
@ BMO_ERROR_FATAL
@ BMO_ERROR_CANCEL
bool BMO_error_pop(BMesh *bm, const char **r_msg, BMOperator **r_op, eBMOpErrorLevel *r_level)
#define BM_elem_index_get(ele)
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const StringRef name)
void * BM_iter_at_index(BMesh *bm, const char itype, void *data, int index)
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
#define BM_ITER_MESH_INDEX(ele, iter, bm, itype, indexvar)
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_EDGE
@ BM_LOOPS_OF_FACE
#define BM_ITER_ELEM_INDEX(ele, iter, data, itype, indexvar)
#define BM_iter_new(iter, bm, itype, data)
BMesh * bm
void BM_mesh_elem_hflag_enable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
void BM_elem_select_set(BMesh *bm, BMElem *ele, const bool select)
void BM_mesh_select_flush(BMesh *bm)
void BM_mesh_select_mode_flush_ex(BMesh *bm, const short selectmode, eBMSelectionFlushFLags flags)
Select Mode Flush.
BMFace * BM_mesh_active_face_get(BMesh *bm, const bool is_sloppy, const bool is_selected)
void BM_mesh_select_mode_clean(BMesh *bm)
void BM_mesh_deselect_flush(BMesh *bm)
void BM_mesh_elem_hflag_disable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
#define BM_elem_hide_set(bm, ele, hide)
@ BM_SELECT_LEN_FLUSH_RECALC_ALL
void BM_mesh_data_free(BMesh *bm)
BMesh Free Mesh Data.
BMVert * BM_vert_at_index_find_or_table(BMesh *bm, const int index)
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
BMEdge * BM_edge_at_index_find_or_table(BMesh *bm, const int index)
bool BM_mesh_elem_table_check(BMesh *bm)
BMFace * BM_face_at_index_find_or_table(BMesh *bm, const int index)
BLI_INLINE BMVert * BM_vert_at_index(BMesh *bm, const int index)
BLI_INLINE BMFace * BM_face_at_index(BMesh *bm, const int index)
void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *mesh, const BMeshToMeshParams *params)
void BM_mesh_normals_update_ex(BMesh *bm, const BMeshNormalsUpdate_Params *params)
BMesh Compute Normals.
void BM_lnorspace_invalidate(BMesh *bm, const bool do_invalidate_all)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
bool BMO_op_vinitf(BMesh *bm, BMOperator *op, int flag, const char *fmt, va_list vlist)
void BMO_slot_buffer_hflag_enable(BMesh *bm, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, char hflag, bool do_flush)
BMO_FLAG_BUFFER.
void BMO_slot_buffer_hflag_disable(BMesh *bm, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, char hflag, bool do_flush)
BMO_FLAG_BUFFER.
BMOpSlot * BMO_slot_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *identifier)
BMESH OPSTACK GET SLOT.
void BMO_op_exec(BMesh *bm, BMOperator *op)
BMESH OPSTACK EXEC OP.
bool BMO_op_initf(BMesh *bm, BMOperator *op, int flag, const char *fmt,...)
void BMO_op_finish(BMesh *bm, BMOperator *op)
BMESH OPSTACK FINISH OP.
#define BMO_FLAG_DEFAULTS
ATTR_WARN_UNUSED_RESULT const void * element
float BM_face_calc_area_uv_signed(const BMFace *f, int cd_loop_uv_offset)
bool BM_edge_in_face(const BMEdge *e, const BMFace *f)
BMFace * BM_face_exists(BMVert *const *varr, int len)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
BMUVOffsets BM_uv_map_offsets_get(const BMesh *bm)
BPy_StructRNA * depsgraph
int64_t size() const
Definition BLI_array.hh:245
const T * data() const
Definition BLI_array.hh:301
int64_t size() const
IndexRange index_range() const
bool EDBM_vert_color_check(BMEditMesh *em)
#define INVALID_ISLAND
UvElementMap * BM_uv_element_map_create(BMesh *bm, const Scene *scene, const bool uv_selected, const bool use_winding, const bool use_seams, const bool do_islands)
BMFace * EDBM_uv_active_face_get(BMEditMesh *em, const bool sloppy, const bool selected)
void EDBM_redo_state_restore_and_free(BMBackup *backup, BMEditMesh *em, bool recalc_looptris)
static BMVert * cache_mirr_intptr_as_bmvert(const intptr_t *index_lookup, int index)
UvMapVert * BM_uv_vert_map_at_index(UvVertMap *vmap, uint v)
void EDBM_update(Mesh *mesh, const EDBMUpdate_Params *params)
void EDBM_select_more(BMEditMesh *em, const bool use_face_step)
UvElement * BM_uv_element_get(const UvElementMap *element_map, const BMLoop *l)
bool EDBM_op_callf(BMEditMesh *em, wmOperator *op, const char *fmt,...)
static void bm_uv_assign_island(UvElementMap *element_map, UvElement *element, int nisland, uint *map, UvElement *islandbuf, int islandbufsize)
#define BM_CD_LAYER_ID
void EDBM_flag_enable_all(BMEditMesh *em, const char hflag)
bool EDBM_op_init(BMEditMesh *em, BMOperator *bmop, wmOperator *op, const char *fmt,...)
BMVert * EDBM_verts_mirror_get(BMEditMesh *em, BMVert *v)
bool EDBM_op_call_and_selectf(BMEditMesh *em, wmOperator *op, const char *select_slot_out, const bool select_extend, const char *fmt,...)
int BM_uv_element_get_unique_index(UvElementMap *element_map, UvElement *child)
void EDBM_mesh_normals_update(BMEditMesh *em)
bool EDBM_mesh_reveal(BMEditMesh *em, bool select)
void EDBM_deselect_flush(BMEditMesh *em)
void EDBM_project_snap_verts(bContext *C, Depsgraph *depsgraph, ARegion *region, Object *obedit, BMEditMesh *em)
void EDBM_mesh_load_ex(Main *bmain, Object *ob, bool free_data)
bool EDBM_op_finish(BMEditMesh *em, BMOperator *bmop, wmOperator *op, const bool do_report)
bool EDBM_uv_check(BMEditMesh *em)
void BM_uv_element_map_free(UvElementMap *element_map)
void EDBM_stats_update(BMEditMesh *em)
bool EDBM_mesh_hide(BMEditMesh *em, bool swap)
void EDBM_verts_mirror_cache_begin_ex(BMEditMesh *em, const int axis, const bool use_self, const bool use_select, const bool respecthide, const bool use_topology, float maxdist, int *r_index)
UvVertMap * BM_uv_vert_map_create(BMesh *bm, const bool use_select)
static void scale_point(float c1[3], const float p[3], const float s)
static bool seam_connected_recursive(BMEdge *edge, const float luv_anchor[2], const float luv_fan[2], const BMLoop *needle, GSet *visited, const int cd_loop_uv_offset)
void EDBM_mesh_free_data(BMEditMesh *em)
void EDBM_selectmode_flush(BMEditMesh *em)
BMElem * EDBM_elem_from_selectmode(BMEditMesh *em, BMVert *eve, BMEdge *eed, BMFace *efa)
void EDBM_mesh_load(Main *bmain, Object *ob)
BMBackup EDBM_redo_state_store(BMEditMesh *em)
BMElem * EDBM_elem_from_index_any_multi(const Scene *scene, ViewLayer *view_layer, uint object_index, uint elem_index, Object **r_obedit)
#define VERT_INTPTR(_v, _i)
void EDBM_verts_mirror_cache_begin(BMEditMesh *em, const int axis, const bool use_self, const bool use_select, const bool respecthide, const bool use_topology)
BMElem * EDBM_elem_from_index_any(BMEditMesh *em, uint index)
bool EDBM_op_call_silentf(BMEditMesh *em, const char *fmt,...)
void EDBM_verts_mirror_cache_end(BMEditMesh *em)
void EDBM_verts_mirror_cache_clear(BMEditMesh *em, BMVert *v)
int EDBM_elem_to_index_any(BMEditMesh *em, BMElem *ele)
BMEdge * EDBM_verts_mirror_get_edge(BMEditMesh *em, BMEdge *e)
UvElement ** BM_uv_element_map_ensure_head_table(UvElementMap *element_map)
void EDBM_select_less(BMEditMesh *em, const bool use_face_step)
BMFace * EDBM_verts_mirror_get_face(BMEditMesh *em, BMFace *f)
static int bm_uv_edge_select_build_islands(UvElementMap *element_map, const Scene *scene, UvElement *islandbuf, uint *map, bool uv_selected, const BMUVOffsets &offsets)
void EDBM_mesh_make_from_mesh(Object *ob, Mesh *src_mesh, const int select_mode, const bool add_key_index)
UvElement * BM_uv_element_get_head(UvElementMap *element_map, UvElement *child)
bool EDBM_view3d_poll(bContext *C)
int * BM_uv_element_map_ensure_unique_index(UvElementMap *element_map)
void EDBM_flag_disable_all(BMEditMesh *em, const char hflag)
#define BM_SEARCH_MAXDIST_MIRR
int EDBM_elem_to_index_any_multi(const Scene *scene, ViewLayer *view_layer, BMEditMesh *em, BMElem *ele, int *r_object_index)
static bool seam_connected(BMLoop *loop_a, BMLoop *loop_b, GSet *visited, int cd_loop_uv_offset)
void EDBM_mesh_make(Object *ob, const int select_mode, const bool add_key_index)
void EDBM_select_flush(BMEditMesh *em)
void EDBM_redo_state_restore(BMBackup *backup, BMEditMesh *em, bool recalc_looptris)
void EDBM_selectmode_to_scene(bContext *C)
static int object_shapenr_basis_index_ensured(const Object *ob)
void EDBM_mesh_normals_update_ex(BMEditMesh *em, const BMeshNormalsUpdate_Params *params)
void EDBM_verts_mirror_apply(BMEditMesh *em, const int sel_from, const int sel_to)
static BMFace * edge_ray_cast(const BMBVHTree *tree, const float co[3], const float dir[3], float *r_hitout, const BMEdge *e)
void BM_uv_vert_map_free(UvVertMap *vmap)
void EDBM_selectmode_flush_ex(BMEditMesh *em, const short selectmode)
static void bm_uv_build_islands(UvElementMap *element_map, BMesh *bm, const Scene *scene, bool uv_selected)
void EDBM_update_extern(Mesh *mesh, const bool do_tessellation, const bool is_destructive)
bool BMBVH_EdgeVisible(const BMBVHTree *tree, const BMEdge *e, const Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const Object *obedit)
void EDBM_redo_state_free(BMBackup *backup)
static bool loop_uv_match(BMLoop *loop, const float luv_a[2], const float luv_b[2], int cd_loop_uv_offset)
KDTree_3d * tree
#define select(A, B, C)
#define printf(...)
#define MEM_SAFE_FREE(v)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static ulong * next
static MirrTopoStore_t mesh_topo_store
Definition meshtools.cc:833
void snap_object_context_destroy(SnapObjectContext *sctx)
eSnapMode snap_object_project_view3d(SnapObjectContext *sctx, Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const eSnapMode snap_to, const SnapObjectParams *params, const float init_co[3], const float mval[2], const float prev_co[3], float *dist_px, float r_loc[3], float r_no[3])
SnapObjectContext * snap_object_context_create(Scene *scene, int flag)
#define swap(a, b)
Definition sort.cc:59
void * regiondata
BMesh * bmcopy
Definition ED_mesh.hh:509
BMVert * v1
BMVert * v2
short selectmode
BMHeader head
struct BMVert * v
struct BMEdge * e
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
eBMOpSlotSubType_Union slot_subtype
struct BMOpSlot slots_out[BMO_OP_MAX_SLOTS]
float co[3]
int totvert
int totfacesel
CustomData vdata
int totedge
char elem_table_dirty
ListBase selected
int totvertsel
int totloop
short selectmode
BMVert ** vtable
int totedgesel
char spacearr_dirty
CustomData ldata
int totface
struct Object * object
ListBase block
MeshRuntimeHandle * runtime
struct Key * key
struct ToolSettings * toolsettings
UvElement * storage
int * island_total_unique_uvs
UvElement ** vertex
UvElement ** head_table
unsigned char flag
unsigned int island
unsigned short loop_of_face_index
UvElement * next
unsigned int face_index
UvMapVert * next
unsigned short loop_of_face_index
UvMapVert * buf
UvMapVert ** vert
struct ReportList * reports
i
Definition text_draw.cc:230
eBMOpSlotSubType_Elem elem
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)