Blender V5.0
bmesh_polygon_edgenet.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11// #define DEBUG_PRINT
12
13#include "MEM_guardedalloc.h"
14
15#include "BLI_alloca.h"
16#include "BLI_kdopbvh.hh"
17#include "BLI_linklist_stack.h"
18#include "BLI_math_geom.h"
19#include "BLI_math_matrix.h"
20#include "BLI_math_vector.h"
21#include "BLI_memarena.h"
22#include "BLI_sort_utils.h"
24#include "BLI_vector.hh"
25
26#include "BKE_customdata.hh"
27
28#include "bmesh.hh"
30
31/* -------------------------------------------------------------------- */
41
42/* NOTE: All these flags _must_ be cleared on exit. */
43
44/* face is a part of the edge-net (including the original face we're splitting) */
45#define FACE_NET _FLAG_WALK
46/* edge is a part of the edge-net we're filling */
47#define EDGE_NET _FLAG_WALK
48/* tag verts we've visit */
49#define VERT_VISIT _FLAG_WALK
50#define VERT_IN_QUEUE _FLAG_WALK_ALT
51
52struct VertOrder {
53 float angle;
55};
56
58{
59 uint count = 0;
60 BMLoop *l;
61
62 if ((l = e->l)) {
63 do {
65 count++;
66 }
67 } while ((l = l->radial_next) != e->l);
68 }
69 return count;
70}
71
73{
74 BMLoop *l;
75
76 if ((l = e->l)) {
77 do {
79 return l;
80 }
81 } while ((l = l->radial_next) != e->l);
82 }
83 return nullptr;
84}
85
86static void normalize_v2_m3_v3v3(float out[2],
87 const float axis_mat[3][3],
88 const float v1[3],
89 const float v2[3])
90{
91 float dir[3];
92 sub_v3_v3v3(dir, v1, v2);
93 mul_v2_m3v3(out, axis_mat, dir);
95}
96
102 const float face_normal[3],
103 const float face_normal_matrix[3][3],
104 BMEdge *e_pair[2])
105{
106 /* Always find one boundary edge (to determine winding)
107 * and one wire (if available), otherwise another boundary.
108 */
109
110 /* detect winding */
111 BMLoop *l_walk;
112 bool swap;
113
114 BLI_SMALLSTACK_DECLARE(edges_boundary, BMEdge *);
115 BLI_SMALLSTACK_DECLARE(edges_wire, BMEdge *);
116 int edges_boundary_len = 0;
117 int edges_wire_len = 0;
118
119 {
120 BMEdge *e, *e_first;
121 e = e_first = v_init->e;
122 do {
125 if (count == 1) {
126 BLI_SMALLSTACK_PUSH(edges_boundary, e);
127 edges_boundary_len++;
128 }
129 else if (count == 0) {
130 BLI_SMALLSTACK_PUSH(edges_wire, e);
131 edges_wire_len++;
132 }
133 }
134 } while ((e = BM_DISK_EDGE_NEXT(e, v_init)) != e_first);
135 }
136
137 /* first edge should always be boundary */
138 if (edges_boundary_len == 0) {
139 return false;
140 }
141 e_pair[0] = static_cast<BMEdge *>(BLI_SMALLSTACK_POP(edges_boundary));
142
143 /* use to hold boundary OR wire edges */
144 BLI_SMALLSTACK_DECLARE(edges_search, BMEdge *);
145
146 /* attempt one boundary and one wire, or 2 boundary */
147 if (edges_wire_len == 0) {
148 if (edges_boundary_len > 1) {
149 e_pair[1] = static_cast<BMEdge *>(BLI_SMALLSTACK_POP(edges_boundary));
150
151 if (edges_boundary_len > 2) {
152 BLI_SMALLSTACK_SWAP(edges_search, edges_boundary);
153 }
154 }
155 else {
156 /* one boundary and no wire */
157 return false;
158 }
159 }
160 else {
161 e_pair[1] = static_cast<BMEdge *>(BLI_SMALLSTACK_POP(edges_wire));
162 if (edges_wire_len > 1) {
163 BLI_SMALLSTACK_SWAP(edges_search, edges_wire);
164 }
165 }
166
167 /* if we swapped above, search this list for the best edge */
168 if (!BLI_SMALLSTACK_IS_EMPTY(edges_search)) {
169 /* find the best edge in 'edge_list' to use for 'e_pair[1]' */
170 const BMVert *v_prev = BM_edge_other_vert(e_pair[0], v_init);
171 const BMVert *v_next = BM_edge_other_vert(e_pair[1], v_init);
172
173 float dir_prev[2], dir_next[2];
174
175 normalize_v2_m3_v3v3(dir_prev, face_normal_matrix, v_prev->co, v_init->co);
176 normalize_v2_m3_v3v3(dir_next, face_normal_matrix, v_next->co, v_init->co);
177 float angle_best_cos = dot_v2v2(dir_next, dir_prev);
178
179 BMEdge *e;
180 while ((e = static_cast<BMEdge *>(BLI_SMALLSTACK_POP(edges_search)))) {
181 v_next = BM_edge_other_vert(e, v_init);
182 float dir_test[2];
183
184 normalize_v2_m3_v3v3(dir_test, face_normal_matrix, v_next->co, v_init->co);
185 const float angle_test_cos = dot_v2v2(dir_prev, dir_test);
186
187 if (angle_test_cos > angle_best_cos) {
188 angle_best_cos = angle_test_cos;
189 e_pair[1] = e;
190 }
191 }
192 }
193
194 /* flip based on winding */
195 l_walk = bm_edge_flagged_radial_first(e_pair[0]);
196 swap = false;
197 if (face_normal == l_walk->f->no) {
198 swap = !swap;
199 }
200 if (l_walk->v != v_init) {
201 swap = !swap;
202 }
203 if (swap) {
204 std::swap(e_pair[0], e_pair[1]);
205 }
206
207 return true;
208}
209
219{
220 int edges_boundary_len = 0;
221 int edges_wire_len = 0;
222
223 {
224 BMEdge *e, *e_first;
225 e = e_first = v_init->e;
226 do {
229 if (count == 1) {
230 edges_boundary_len++;
231 }
232 else if (count == 0) {
233 edges_wire_len++;
234 }
235 }
236 } while ((e = BM_DISK_EDGE_NEXT(e, v_init)) != e_first);
237 }
238
239 /* first edge should always be boundary */
240 if (edges_boundary_len == 0) {
241 return false;
242 }
243
244 /* attempt one boundary and one wire, or 2 boundary */
245 if (edges_wire_len == 0) {
246 if (edges_boundary_len >= 2) {
247 /* pass */
248 }
249 else {
250 /* one boundary and no wire */
251 return false;
252 }
253 }
254 else {
255 /* pass */
256 }
257
258 return true;
259}
260
262 const float face_normal[3],
263 /* cache to avoid realloc every time */
264 VertOrder *edge_order,
265 const uint edge_order_len,
266 BMEdge *e_pair[2])
267{
268 UNUSED_VARS_NDEBUG(edge_order_len);
269
270/* fast-path for the common case (avoid push-pop).
271 * Also avoids tagging as visited since we know we
272 * can't reach these verts some other way */
273#define USE_FASTPATH_NOFORK
274
275 BMVert *v;
276 BMVert *v_dst;
277 bool found = false;
278
279 VertOrder *eo;
280 STACK_DECLARE(edge_order);
281
282 /* store visited verts so we can clear the visit flag after execution */
283 BLI_SMALLSTACK_DECLARE(vert_visit, BMVert *);
284
285 /* likely this will stay very small
286 * all verts pushed into this stack _must_ have their previous edges set! */
287 BLI_SMALLSTACK_DECLARE(vert_stack, BMVert *);
288 BLI_SMALLSTACK_DECLARE(vert_stack_next, BMVert *);
289
290 STACK_INIT(edge_order, edge_order_len);
291
292 /* start stepping */
293 v = BM_edge_other_vert(e_pair[0], v_init);
294 v->e = e_pair[0];
295 BLI_SMALLSTACK_PUSH(vert_stack, v);
296
297 v_dst = BM_edge_other_vert(e_pair[1], v_init);
298
299#ifdef DEBUG_PRINT
300 printf("%s: vert (search) %d\n", __func__, BM_elem_index_get(v_init));
301#endif
302
303 /* This loop will keep stepping over the best possible edge,
304 * in most cases it finds the direct route to close the face.
305 *
306 * In cases where paths can't be closed,
307 * alternatives are stored in the 'vert_stack'.
308 */
309 while ((v = static_cast<BMVert *>(BLI_SMALLSTACK_POP_EX(vert_stack, vert_stack_next)))) {
310#ifdef USE_FASTPATH_NOFORK
311 walk_nofork:
312#else
313 BLI_SMALLSTACK_PUSH(vert_visit, v);
315#endif
316
317 BLI_assert(STACK_SIZE(edge_order) == 0);
318
319 /* check if we're done! */
320 if (v == v_dst) {
321 found = true;
322 goto finally;
323 }
324
325 BMEdge *e_next, *e_first;
326 e_first = v->e;
327 e_next = BM_DISK_EDGE_NEXT(e_first, v); /* always skip this verts edge */
328
329 /* in rare cases there may be edges with a single connecting vertex */
330 if (e_next != e_first) {
331 do {
332 if (BM_ELEM_API_FLAG_TEST(e_next, EDGE_NET) && (bm_edge_flagged_radial_count(e_next) < 2))
333 {
334 BMVert *v_next;
335
336 v_next = BM_edge_other_vert(e_next, v);
337 BLI_assert(v->e != e_next);
338
339#ifdef DEBUG_PRINT
340 /* indent and print */
341 {
342 BMVert *_v = v;
343 do {
344 printf(" ");
345 } while ((_v = BM_edge_other_vert(_v->e, _v)) != v_init);
346 printf("vert %d -> %d (add=%d)\n",
348 BM_elem_index_get(v_next),
349 BM_ELEM_API_FLAG_TEST(v_next, VERT_VISIT) == 0);
350 }
351#endif
352
353 if (!BM_ELEM_API_FLAG_TEST(v_next, VERT_VISIT)) {
354 eo = STACK_PUSH_RET_PTR(edge_order);
355 eo->v = v_next;
356
357 v_next->e = e_next;
358 }
359 }
360 } while ((e_next = BM_DISK_EDGE_NEXT(e_next, v)) != e_first);
361 }
362
363#ifdef USE_FASTPATH_NOFORK
364 if (STACK_SIZE(edge_order) == 1) {
365 eo = STACK_POP_PTR(edge_order);
366 v = eo->v;
367
368 goto walk_nofork;
369 }
370#endif
371
372 /* sort by angle if needed */
373 if (STACK_SIZE(edge_order) > 1) {
374 uint j;
375 BMVert *v_prev = BM_edge_other_vert(v->e, v);
376
377 for (j = 0; j < STACK_SIZE(edge_order); j++) {
379 v_prev->co, v->co, edge_order[j].v->co, face_normal);
380 }
381 qsort(edge_order, STACK_SIZE(edge_order), sizeof(VertOrder), BLI_sortutil_cmp_float_reverse);
382
383#ifdef USE_FASTPATH_NOFORK
384 /* only tag forks */
385 BLI_SMALLSTACK_PUSH(vert_visit, v);
387#endif
388 }
389
390 while ((eo = STACK_POP_PTR(edge_order))) {
391 BLI_SMALLSTACK_PUSH(vert_stack_next, eo->v);
392 }
393
394 if (!BLI_SMALLSTACK_IS_EMPTY(vert_stack_next)) {
395 BLI_SMALLSTACK_SWAP(vert_stack, vert_stack_next);
396 }
397 }
398
399finally:
400 /* clear flag for next execution */
401 while ((v = static_cast<BMVert *>(BLI_SMALLSTACK_POP(vert_visit)))) {
403 }
404
405 return found;
406
407#undef USE_FASTPATH_NOFORK
408}
409
411 const float face_normal[3],
412 const float face_normal_matrix[3][3],
413 /* cache to avoid realloc every time */
414 VertOrder *edge_order,
415 const uint edge_order_len,
416 BMVert **r_face_verts,
417 int *r_face_verts_len)
418{
419 BMEdge *e_pair[2];
420 BMVert *v;
421
422 if (!bm_face_split_edgenet_find_loop_pair(v_init, face_normal, face_normal_matrix, e_pair)) {
423 return false;
424 }
425
426 BLI_assert((bm_edge_flagged_radial_count(e_pair[0]) == 1) ||
427 (bm_edge_flagged_radial_count(e_pair[1]) == 1));
428
430 v_init, face_normal, edge_order, edge_order_len, e_pair))
431 {
432 uint i = 0;
433
434 r_face_verts[i++] = v_init;
435 v = BM_edge_other_vert(e_pair[1], v_init);
436 do {
437 r_face_verts[i++] = v;
438 } while ((v = BM_edge_other_vert(v->e, v)) != v_init);
439 *r_face_verts_len = i;
440 return (i > 2) ? true : false;
441 }
442 return false;
443}
444
446 BMFace *f,
447 BMEdge **edge_net,
448 const int edge_net_len,
449 blender::Vector<BMFace *> *r_face_arr)
450{
451 /* re-use for new face verts */
452 BMVert **face_verts;
453 int face_verts_len;
454
455 BMVert **vert_queue;
456 STACK_DECLARE(vert_queue);
457 int i;
458
459 VertOrder *edge_order;
460 const uint edge_order_len = edge_net_len + 2;
461
462 BMVert *v;
463
464 BMLoop *l_iter, *l_first;
465
466 if (!edge_net_len) {
467 if (r_face_arr) {
468 r_face_arr->clear_and_shrink();
469 }
470 return false;
471 }
472
473 /* These arrays used to be stack memory, however they can be
474 * large for single faces with complex edge-nets, see: #65980. */
475
476 /* over-alloc (probably 2-4 is only used in most cases), for the biggest-fan */
477 edge_order = MEM_malloc_arrayN<VertOrder>(edge_order_len, __func__);
478
479 /* use later */
480 face_verts = static_cast<BMVert **>(
481 MEM_mallocN(sizeof(*face_verts) * (edge_net_len + f->len), __func__));
482
483 vert_queue = static_cast<BMVert **>(
484 MEM_mallocN(sizeof(vert_queue) * (edge_net_len + f->len), __func__));
485 STACK_INIT(vert_queue, f->len + edge_net_len);
486
489
490#ifndef NDEBUG
491 for (i = 0; i < edge_net_len; i++) {
492 BLI_assert(BM_ELEM_API_FLAG_TEST(edge_net[i], EDGE_NET) == 0);
493 BLI_assert(BM_edge_in_face(edge_net[i], f) == false);
494 }
495 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
496 do {
498 } while ((l_iter = l_iter->next) != l_first);
499#endif
500
501 /* NOTE: 'VERT_IN_QUEUE' is often not needed at all,
502 * however in rare cases verts are added multiple times to the queue,
503 * that on its own is harmless but in _very_ rare cases,
504 * the queue will overflow its maximum size,
505 * so we better be strict about this! see: #51539 */
506
507 for (i = 0; i < edge_net_len; i++) {
511 }
512 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
513 do {
516 } while ((l_iter = l_iter->next) != l_first);
517
518 float face_normal_matrix[3][3];
519 axis_dominant_v3_to_m3(face_normal_matrix, f->no);
520
521 /* any vert can be used to begin with */
522 STACK_PUSH(vert_queue, l_first->v);
524
526 while ((v = STACK_POP(vert_queue))) {
529 v, f->no, face_normal_matrix, edge_order, edge_order_len, face_verts, &face_verts_len))
530 {
531 BMFace *f_new;
532
533 f_new = BM_face_create_verts(bm, face_verts, face_verts_len, f, BM_CREATE_NOP, false);
534
535 for (i = 0; i < edge_net_len; i++) {
537 }
538
539 if (f_new) {
540 face_arr.append(f_new);
541 copy_v3_v3(f_new->no, f->no);
542
543 /* warning, normally don't do this,
544 * its needed for mesh intersection - which tracks face-sides based on selection */
545 f_new->head.hflag = f->head.hflag;
546 if (f->head.hflag & BM_ELEM_SELECT) {
547 bm->totfacesel++;
548 }
549
551
552 /* add new verts to keep finding loops for
553 * (verts between boundary and manifold edges) */
554 l_iter = l_first = BM_FACE_FIRST_LOOP(f_new);
555 do {
556 /* Avoid adding to queue multiple times (not common but happens). */
557 if (!BM_ELEM_API_FLAG_TEST(l_iter->v, VERT_IN_QUEUE) &&
559 {
560 STACK_PUSH(vert_queue, l_iter->v);
562 }
563 } while ((l_iter = l_iter->next) != l_first);
564 }
565 }
566 }
567
568 if (CustomData_has_math(&bm->ldata)) {
569 /* reuse VERT_VISIT here to tag vert's already interpolated */
570 BMIter iter;
571 BMLoop *l_other;
572
573 /* See: #BM_loop_interp_from_face for similar logic. */
574 void **blocks = BLI_array_alloca(blocks, f->len);
575 float (*cos_2d)[2] = BLI_array_alloca(cos_2d, f->len);
576 float *w = BLI_array_alloca(w, f->len);
577 float axis_mat[3][3];
578 float co[2];
579
580 /* interior loops */
581 axis_dominant_v3_to_m3(axis_mat, f->no);
582
583 /* first simply copy from existing face */
584 i = 0;
585 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
586 do {
587 BM_ITER_ELEM (l_other, &iter, l_iter->v, BM_LOOPS_OF_VERT) {
588 if ((l_other->f != f) && BM_ELEM_API_FLAG_TEST(l_other->f, FACE_NET)) {
589 CustomData_bmesh_copy_block(bm->ldata, l_iter->head.data, &l_other->head.data);
590 }
591 }
592 /* tag not to interpolate */
594
595 mul_v2_m3v3(cos_2d[i], axis_mat, l_iter->v->co);
596 blocks[i] = l_iter->head.data;
597
598 } while ((void)i++, (l_iter = l_iter->next) != l_first);
599
600 for (i = 0; i < edge_net_len; i++) {
601 BM_ITER_ELEM (v, &iter, edge_net[i], BM_VERTS_OF_EDGE) {
603 BMIter liter;
604
606
607 /* interpolate this loop, then copy to the rest */
608 l_first = nullptr;
609
610 BM_ITER_ELEM (l_iter, &liter, v, BM_LOOPS_OF_VERT) {
611 if (BM_ELEM_API_FLAG_TEST(l_iter->f, FACE_NET)) {
612 if (l_first == nullptr) {
613 mul_v2_m3v3(co, axis_mat, v->co);
614 interp_weights_poly_v2(w, cos_2d, f->len, co);
616 &bm->ldata, (const void **)blocks, w, f->len, l_iter->head.data);
617 l_first = l_iter;
618 }
619 else {
620 CustomData_bmesh_copy_block(bm->ldata, l_first->head.data, &l_iter->head.data);
621 }
622 }
623 }
624 }
625 }
626 }
627 }
628
629 /* cleanup */
630 for (i = 0; i < edge_net_len; i++) {
632 /* from interp only */
633 BM_ELEM_API_FLAG_DISABLE(edge_net[i]->v1, VERT_VISIT);
635 }
636 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
637 do {
639 /* from interp only */
641 } while ((l_iter = l_iter->next) != l_first);
642
643 if (!face_arr.is_empty()) {
644 bmesh_face_swap_data(f, face_arr[0]);
645 BM_face_kill(bm, face_arr[0]);
646 face_arr[0] = f;
647 }
648 else {
650 }
651
652 for (BMFace *face : face_arr) {
654 }
655
656 if (r_face_arr) {
657 *r_face_arr = std::move(face_arr);
658 }
659
660 MEM_freeN(edge_order);
661 MEM_freeN(face_verts);
662 MEM_freeN(vert_queue);
663
664 return true;
665}
666
667#undef FACE_NET
668#undef VERT_VISIT
669#undef EDGE_NET
670
672
673/* -------------------------------------------------------------------- */
686
687#define USE_PARTIAL_CONNECT
688
689#define VERT_IS_VALID BM_ELEM_INTERNAL_TAG
690
691/* can be X or Y */
692#define SORT_AXIS 0
693
695 const BMVert *v_a,
696 const BMVert *v_b,
697 float r_isect[2])
698{
699 /* This bias seems like it could be too large,
700 * mostly its not needed, see #52329 for example where it is. */
701 const float endpoint_bias = 1e-4f;
703 v_a->co, v_b->co, e->v1->co, e->v2->co, endpoint_bias, r_isect) == 1) &&
704 ((e->v1 != v_a) && (e->v2 != v_a) && (e->v1 != v_b) && (e->v2 != v_b)));
705}
706
707BLI_INLINE int axis_pt_cmp(const float pt_a[2], const float pt_b[2])
708{
709 if (pt_a[0] < pt_b[0]) {
710 return -1;
711 }
712 if (pt_a[0] > pt_b[0]) {
713 return 1;
714 }
715 if (pt_a[1] < pt_b[1]) {
716 return -1;
717 }
718 if (pt_a[1] > pt_b[1]) {
719 return 1;
720 }
721 return 0;
722}
723
730 LinkNode edge_links; /* keep first */
732
733 /* Set the following vars once we have >1 groups */
734
735 /* when an edge in a previous group connects to this one,
736 * so there's no need to create one pointing back. */
738
739 /* verts in the group which has the lowest & highest values,
740 * the lower vertex is connected to the first edge */
741 struct {
743 /* used for sorting only */
744 float min_axis[2];
745 float max_axis[2];
747};
748
749static int group_min_cmp_fn(const void *p1, const void *p2)
750{
751 const EdgeGroupIsland *g1 = *(EdgeGroupIsland **)p1;
752 const EdgeGroupIsland *g2 = *(EdgeGroupIsland **)p2;
753 /* min->co[SORT_AXIS] hasn't been applied yet */
754 int test = axis_pt_cmp(g1->vert_span.min_axis, g2->vert_span.min_axis);
755 if (UNLIKELY(test == 0)) {
757 }
758 return test;
759}
760
770
778
779static void bvhtree_test_edges_isect_2d_vert_cb(void *user_data,
780 int index,
781 const BVHTreeRay * /*ray*/,
782 BVHTreeRayHit *hit)
783{
785 const BMEdge *e = data->edge_arr[index];
786 const int v1_index = BM_elem_index_get(e->v1);
787 float co_isect[2];
788
789 if (edge_isect_verts_point_2d(e, data->v_origin, data->v_other, co_isect)) {
790 const float t = line_point_factor_v2(co_isect, data->v_origin->co, data->v_other->co);
791 const float dist_new = data->dist_orig * t;
792 /* avoid float precision issues, possible this is greater,
793 * check above zero to allow some overlap
794 * (and needed for partial-connect which will overlap vertices) */
795 if (LIKELY((dist_new < hit->dist) && (dist_new > 0.0f))) {
796 /* v1/v2 will both be in the same group */
797 if (v1_index < int(data->vert_range[0]) || v1_index >= int(data->vert_range[1])) {
798 hit->dist = dist_new;
799 hit->index = index;
800 }
801 }
802 }
803}
804
805static void bvhtree_test_edges_isect_2d_ray_cb(void *user_data,
806 int index,
807 const BVHTreeRay *ray,
808 BVHTreeRayHit *hit)
809{
810 Edges_VertRay_BVHTreeTest *data = static_cast<Edges_VertRay_BVHTreeTest *>(user_data);
811 const BMEdge *e = data->edge_arr[index];
812
813 /* direction is normalized, so this will be the distance */
814 float dist_new;
816 data->v_origin->co, ray->direction, e->v1->co, e->v2->co, &dist_new, nullptr))
817 {
818 /* avoid float precision issues, possible this is greater,
819 * check above zero to allow some overlap
820 * (and needed for partial-connect which will overlap vertices) */
821 if (LIKELY(dist_new < hit->dist && (dist_new > 0.0f))) {
822 if (e->v1 != data->v_origin && e->v2 != data->v_origin) {
823 const int v1_index = BM_elem_index_get(e->v1);
824 /* v1/v2 will both be in the same group */
825 if (v1_index < int(data->vert_range[0]) || v1_index >= int(data->vert_range[1])) {
826 hit->dist = dist_new;
827 hit->index = index;
828 }
829 }
830 }
831 }
832}
833
850
852 BMVert *v_origin,
853 BMVert *v_other)
854{
855 int index;
856
857 BVHTreeRayHit hit = {0};
858 float dir[3];
859
860 sub_v2_v2v2(dir, v_other->co, v_origin->co);
861 dir[2] = 0.0f;
862 hit.index = -1;
863 hit.dist = normalize_v2(dir);
864
865 Edges_VertVert_BVHTreeTest user_data = {0};
866 user_data.dist_orig = hit.dist;
867 user_data.edge_arr = args->edge_arr;
868 user_data.v_origin = v_origin;
869 user_data.v_other = v_other;
870 user_data.vert_range = args->vert_range;
871
872 index = BLI_bvhtree_ray_cast_ex(args->bvhtree,
873 v_origin->co,
874 dir,
875 0.0f,
876 &hit,
878 &user_data,
879 0);
880
881 BMEdge *e_hit = (index != -1) ? args->edge_arr[index] : nullptr;
882
883 /* check existing connections (no spatial optimization here since we're continually adding). */
884 if (LIKELY(index == -1)) {
885 float t_best = 1.0f;
886 for (uint i = 0; i < args->edge_arr_new_len; i++) {
887 float co_isect[2];
888 if (UNLIKELY(edge_isect_verts_point_2d(args->edge_arr_new[i], v_origin, v_other, co_isect)))
889 {
890 const float t_test = line_point_factor_v2(co_isect, v_origin->co, v_other->co);
891 if (t_test < t_best) {
892 t_best = t_test;
893
894 e_hit = args->edge_arr_new[i];
895 }
896 }
897 }
898 }
899
900 return e_hit;
901}
902
908 BMVert *v_origin,
909 const float dir[3])
910{
911 int index;
912 BVHTreeRayHit hit = {0};
913
915
916 hit.index = -1;
918
919 Edges_VertRay_BVHTreeTest user_data = {nullptr};
920 user_data.edge_arr = args->edge_arr;
921 user_data.v_origin = v_origin;
922 user_data.vert_range = args->vert_range;
923
924 index = BLI_bvhtree_ray_cast_ex(args->bvhtree,
925 v_origin->co,
926 dir,
927 0.0f,
928 &hit,
930 &user_data,
931 0);
932
933 BMEdge *e_hit = (index != -1) ? args->edge_arr[index] : nullptr;
934
935 /* check existing connections (no spatial optimization here since we're continually adding). */
936 if (LIKELY(index != -1)) {
937 for (uint i = 0; i < args->edge_arr_new_len; i++) {
938 BMEdge *e = args->edge_arr_new[i];
939 float dist_new;
940 if (isect_ray_seg_v2(v_origin->co, dir, e->v1->co, e->v2->co, &dist_new, nullptr)) {
941 if (e->v1 != v_origin && e->v2 != v_origin) {
942 /* avoid float precision issues, possible this is greater */
943 if (LIKELY(dist_new < hit.dist)) {
944 hit.dist = dist_new;
945
946 e_hit = args->edge_arr_new[i];
947 }
948 }
949 }
950 }
951 }
952
953 return e_hit;
954}
955
957 BMVert *v_origin,
958 /* false = negative, true = positive */
959 bool direction_sign)
960{
974
975 float dir[3] = {0, 0, 0};
976 dir[SORT_AXIS] = direction_sign ? 1.0f : -1.0f;
977 BMEdge *e_hit = test_edges_isect_2d_ray(args, v_origin, dir);
978 BMVert *v_other = nullptr;
979
980 if (e_hit) {
981 BMVert *v_other_fallback = nullptr;
982
983 BLI_SMALLSTACK_DECLARE(vert_search, BMVert *);
984
985 /* ensure we never add verts multiple times (not all that likely - but possible) */
986 BLI_SMALLSTACK_DECLARE(vert_blacklist, BMVert *);
987
988 do {
989 BMVert *v_pair[2];
990 /* ensure the closest vertex is popped back off the stack first */
991 if (len_squared_v2v2(v_origin->co, e_hit->v1->co) >
992 len_squared_v2v2(v_origin->co, e_hit->v2->co))
993 {
994 ARRAY_SET_ITEMS(v_pair, e_hit->v1, e_hit->v2);
995 }
996 else {
997 ARRAY_SET_ITEMS(v_pair, e_hit->v2, e_hit->v1);
998 }
999
1000 for (int j = 0; j < 2; j++) {
1001 BMVert *v_iter = v_pair[j];
1002 if (BM_elem_flag_test(v_iter, VERT_IS_VALID)) {
1003 if (direction_sign ? (v_iter->co[SORT_AXIS] > v_origin->co[SORT_AXIS]) :
1004 (v_iter->co[SORT_AXIS] < v_origin->co[SORT_AXIS]))
1005 {
1006 BLI_SMALLSTACK_PUSH(vert_search, v_iter);
1007 BLI_SMALLSTACK_PUSH(vert_blacklist, v_iter);
1009 }
1010 }
1011 }
1012 v_other_fallback = v_other;
1013
1014 } while ((v_other = static_cast<BMVert *>(BLI_SMALLSTACK_POP(vert_search))) &&
1015 (e_hit = test_edges_isect_2d_vert(args, v_origin, v_other)));
1016
1017 if (v_other == nullptr) {
1018 printf("Using fallback\n");
1019 v_other = v_other_fallback;
1020 }
1021
1022 /* reset the blacklist flag, for future use */
1023 BMVert *v;
1024 while ((v = static_cast<BMVert *>(BLI_SMALLSTACK_POP(vert_blacklist)))) {
1026 }
1027 }
1028
1029 /* if we reach this line, v_other is either the best vertex or its nullptr */
1030 return v_other ? BM_elem_index_get(v_other) : -1;
1031}
1032
1037#ifdef USE_PARTIAL_CONNECT
1038
1043static bool test_tagged_and_notface(BMEdge *e, void *fptr)
1044{
1045 BMFace *f = (BMFace *)fptr;
1047}
1048
1057{
1058 /* -------------------------------------------------------------------- */
1059 /* Initial check that we may be a delimiting vert (keep this fast) */
1060
1061 /* initial check - see if we have 3+ flagged edges attached to 'v_delimit'
1062 * if not, we can early exit */
1063 LinkNode *e_delimit_list = nullptr;
1064 uint e_delimit_list_len = 0;
1065
1066# define EDGE_NOT_IN_STACK BM_ELEM_INTERNAL_TAG
1067# define VERT_NOT_IN_STACK BM_ELEM_INTERNAL_TAG
1068
1069# define FOREACH_VERT_EDGE(v_, e_, body_) \
1070 { \
1071 BMEdge *e_ = v_->e; \
1072 do { \
1073 body_ \
1074 } while ((e_ = BM_DISK_EDGE_NEXT(e_, v_)) != v_->e); \
1075 } \
1076 ((void)0)
1077
1078 /* start with face edges, since we need to split away wire-only edges */
1079 BMEdge *e_face_init = nullptr;
1080
1081 FOREACH_VERT_EDGE(v_delimit, e_iter, {
1082 if (BM_elem_flag_test(e_iter, EDGE_NOT_IN_STACK)) {
1084 BLI_linklist_prepend_alloca(&e_delimit_list, e_iter);
1085 e_delimit_list_len++;
1086 if (e_iter->l != nullptr && BM_edge_in_face(e_iter, f)) {
1087 e_face_init = e_iter;
1088 }
1089 }
1090 });
1091
1092 /* skip typical edge-chain verts */
1093 if (LIKELY(e_delimit_list_len <= 2)) {
1094 return nullptr;
1095 }
1096
1097 /* -------------------------------------------------------------------- */
1098 /* Complicated stuff starts now! */
1099
1100 /* Store connected vertices for restoring the flag */
1101 LinkNode *vert_stack = nullptr;
1102 BLI_linklist_prepend_alloca(&vert_stack, v_delimit);
1104
1105 /* Walk the net... */
1106 {
1107 BLI_SMALLSTACK_DECLARE(search, BMVert *);
1108 BMVert *v_other = BM_edge_other_vert(e_face_init ? e_face_init : v_delimit->e, v_delimit);
1109
1110 BLI_SMALLSTACK_PUSH(search, v_other);
1111 if (BM_elem_flag_test(v_other, VERT_NOT_IN_STACK)) {
1113 BLI_linklist_prepend_alloca(&vert_stack, v_other);
1114 }
1115
1116 while ((v_other = static_cast<BMVert *>(BLI_SMALLSTACK_POP(search)))) {
1118 BMEdge *e_iter = v_other->e;
1119 do {
1120 BMVert *v_step = BM_edge_other_vert(e_iter, v_other);
1121 if (BM_elem_flag_test(e_iter, EDGE_NOT_IN_STACK)) {
1122 if (BM_elem_flag_test(v_step, VERT_NOT_IN_STACK)) {
1124 BLI_SMALLSTACK_PUSH(search, v_step);
1125 BLI_linklist_prepend_alloca(&vert_stack, v_step);
1126 }
1127 }
1128 } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v_other)) != v_other->e);
1129 }
1130 }
1131
1132 /* Detect if this is a delimiter
1133 * by checking if we didn't walk any of edges connected to 'v_delimit'. */
1134 bool is_delimit = false;
1135 FOREACH_VERT_EDGE(v_delimit, e_iter, {
1136 BMVert *v_step = BM_edge_other_vert(e_iter, v_delimit);
1137 if (BM_elem_flag_test(v_step, VERT_NOT_IN_STACK) && !BM_edge_in_face(e_iter, f)) {
1138 is_delimit = true; /* if one vertex is valid - we have a mix */
1139 }
1140 else {
1141 /* match the vertex flag (only for edges around 'v_delimit') */
1143 }
1144 });
1145
1146# undef FOREACH_VERT_EDGE
1147
1148 /* Execute the split */
1149 BMVert *v_split = nullptr;
1150 if (is_delimit) {
1151 v_split = BM_vert_create(bm, v_delimit->co, nullptr, eBMCreateFlag(0));
1154
1155 BLI_assert(v_delimit->e != nullptr);
1156
1157/* Degenerate, avoid eternal loop, see: #59074. */
1158# if 0
1159 BLI_assert(v_split->e != nullptr);
1160# else
1161 if (UNLIKELY(v_split->e == nullptr)) {
1162 BM_vert_kill(bm, v_split);
1163 v_split = nullptr;
1164 }
1165# endif
1166 }
1167
1168 /* Restore flags */
1169 do {
1171 } while ((vert_stack = vert_stack->next));
1172
1173 do {
1174 BM_elem_flag_enable((BMEdge *)e_delimit_list->link, EDGE_NOT_IN_STACK);
1175 } while ((e_delimit_list = e_delimit_list->next));
1176
1177# undef EDGE_NOT_IN_STACK
1178# undef VERT_NOT_IN_STACK
1179
1180 return v_split;
1181}
1182
1186static bool bm_vert_partial_connect_check_overlap(const int *remap,
1187 const int v_a_index,
1188 const int v_b_index)
1189{
1190 /* Connected to each other. */
1191 if (UNLIKELY((remap[v_a_index] == v_b_index) || (remap[v_b_index] == v_a_index))) {
1192 return true;
1193 }
1194 return false;
1195}
1196
1197#endif /* USE_PARTIAL_CONNECT */
1198
1200 BMFace *f,
1201 BMEdge **edge_net_init,
1202 const uint edge_net_init_len,
1203 bool use_partial_connect,
1205 BMEdge ***r_edge_net_new,
1206 uint *r_edge_net_new_len)
1207{
1208 /* -------------------------------------------------------------------- */
1220
1221 const uint edge_arr_len = uint(edge_net_init_len) + uint(f->len);
1222 BMEdge **edge_arr = static_cast<BMEdge **>(
1223 BLI_memarena_alloc(mem_arena, sizeof(*edge_arr) * edge_arr_len));
1224 bool ok = false;
1225 uint edge_net_new_len = uint(edge_net_init_len);
1226
1227 memcpy(edge_arr, edge_net_init, sizeof(*edge_arr) * size_t(edge_net_init_len));
1228
1229/* _must_ clear on exit */
1230#define EDGE_NOT_IN_STACK BM_ELEM_INTERNAL_TAG
1231#define VERT_NOT_IN_STACK BM_ELEM_INTERNAL_TAG
1232
1233 {
1234 uint i = edge_net_init_len;
1235 BMLoop *l_iter, *l_first;
1236 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1237 do {
1240 edge_arr[i++] = l_iter->e;
1241 } while ((l_iter = l_iter->next) != l_first);
1242 BLI_assert(i == edge_arr_len);
1243 }
1244
1245 for (uint i = 0; i < edge_arr_len; i++) {
1247 BM_elem_flag_enable(edge_arr[i]->v1, VERT_NOT_IN_STACK);
1249 }
1250
1251#ifdef USE_PARTIAL_CONNECT
1252 /* Split-out delimiting vertices */
1253 struct TempVertPair {
1254 TempVertPair *next;
1255 BMVert *v_temp;
1256 BMVert *v_orig;
1257 };
1258
1259 struct {
1260 TempVertPair *list;
1261 uint len;
1262 int *remap; /* temp -> orig mapping */
1263 } temp_vert_pairs = {nullptr};
1264
1265 if (use_partial_connect) {
1266 for (uint i = 0; i < edge_net_init_len; i++) {
1267 for (uint j = 0; j < 2; j++) {
1268 BMVert *v_delimit = (&edge_arr[i]->v1)[j];
1269 BMVert *v_other;
1270
1271 /* NOTE: remapping will _never_ map a vertex to an already mapped vertex. */
1272 while (UNLIKELY(v_other = bm_face_split_edgenet_partial_connect(bm, v_delimit, f))) {
1273 TempVertPair *tvp = static_cast<TempVertPair *>(
1274 BLI_memarena_alloc(mem_arena, sizeof(*tvp)));
1275 tvp->next = temp_vert_pairs.list;
1276 tvp->v_orig = v_delimit;
1277 tvp->v_temp = v_other;
1278 temp_vert_pairs.list = tvp;
1279 temp_vert_pairs.len++;
1280 }
1281 }
1282 }
1283
1284 if (temp_vert_pairs.len == 0) {
1285 use_partial_connect = false;
1286 }
1287 }
1288#endif /* USE_PARTIAL_CONNECT */
1289
1290 uint group_arr_len = 0;
1291 LinkNode *group_head = nullptr;
1292 {
1293 /* scan 'edge_arr' backwards so the outer face boundary is handled first
1294 * (since its likely to be the largest) */
1295 uint edge_index = (edge_arr_len - 1);
1296 uint edge_in_group_tot = 0;
1297
1298 BLI_SMALLSTACK_DECLARE(vstack, BMVert *);
1299
1300 while (true) {
1301 LinkNode *edge_links = nullptr;
1302 uint unique_verts_in_group = 0, unique_edges_in_group = 0;
1303
1304 /* list of groups */
1305 BLI_assert(BM_elem_flag_test(edge_arr[edge_index]->v1, VERT_NOT_IN_STACK));
1306 BLI_SMALLSTACK_PUSH(vstack, edge_arr[edge_index]->v1);
1307 BM_elem_flag_disable(edge_arr[edge_index]->v1, VERT_NOT_IN_STACK);
1308
1309 BMVert *v_iter;
1310 while ((v_iter = static_cast<BMVert *>(BLI_SMALLSTACK_POP(vstack)))) {
1311 unique_verts_in_group++;
1312
1313 BMEdge *e_iter = v_iter->e;
1314 do {
1315 if (BM_elem_flag_test(e_iter, EDGE_NOT_IN_STACK)) {
1317 unique_edges_in_group++;
1318
1319 BLI_linklist_prepend_arena(&edge_links, e_iter, mem_arena);
1320
1321 BMVert *v_other = BM_edge_other_vert(e_iter, v_iter);
1322 if (BM_elem_flag_test(v_other, VERT_NOT_IN_STACK)) {
1323 BLI_SMALLSTACK_PUSH(vstack, v_other);
1325 }
1326 }
1327 } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v_iter)) != v_iter->e);
1328 }
1329
1330 EdgeGroupIsland *g = static_cast<EdgeGroupIsland *>(
1331 BLI_memarena_alloc(mem_arena, sizeof(*g)));
1332 g->vert_len = unique_verts_in_group;
1333 g->edge_len = unique_edges_in_group;
1334 edge_in_group_tot += unique_edges_in_group;
1335
1336 BLI_linklist_prepend_nlink(&group_head, edge_links, (LinkNode *)g);
1337
1338 group_arr_len++;
1339
1340 if (edge_in_group_tot == edge_arr_len) {
1341 break;
1342 }
1343
1344 /* skip edges in the stack */
1345 while (BM_elem_flag_test(edge_arr[edge_index], EDGE_NOT_IN_STACK) == false) {
1346 BLI_assert(edge_index != 0);
1347 edge_index--;
1348 }
1349 }
1350 }
1351
1352 /* Declare here because of `goto` below. */
1353 BMEdge **edge_net_new = nullptr;
1354 BVHTree *bvhtree = nullptr;
1355 float (*vert_coords_backup)[3] = nullptr;
1356 uint *verts_group_table = nullptr;
1357 BMVert **vert_arr = nullptr;
1358 uint vert_arr_len = 0;
1359 EdgeGroupIsland **group_arr = nullptr;
1360
1361 /* single group - no holes */
1362 if (group_arr_len == 1) {
1363 goto finally;
1364 }
1365
1366 /* -------------------------------------------------------------------- */
1367 /* Previous checks need to be kept fast, since they will run very often,
1368 * now we know there are holes, so calculate a spatial lookup info and
1369 * other per-group data.
1370 */
1371
1372 float axis_mat[3][3];
1373 axis_dominant_v3_to_m3(axis_mat, f->no);
1374
1375#define VERT_IN_ARRAY BM_ELEM_INTERNAL_TAG
1376
1377 group_arr = static_cast<EdgeGroupIsland **>(
1378 BLI_memarena_alloc(mem_arena, sizeof(*group_arr) * group_arr_len));
1379 /* sort groups by lowest value vertex */
1380 {
1381 /* fill 'groups_arr' in reverse order so the boundary face is first */
1382 EdgeGroupIsland **group_arr_p = &group_arr[group_arr_len];
1383
1384 for (EdgeGroupIsland *g = static_cast<EdgeGroupIsland *>((void *)group_head); g;
1386 {
1387 LinkNode *edge_links = static_cast<LinkNode *>(g->edge_links.link);
1388
1389 /* init with *any* different verts */
1390 g->vert_span.min = ((BMEdge *)edge_links->link)->v1;
1391 g->vert_span.max = ((BMEdge *)edge_links->link)->v2;
1392 float min_axis[2] = {FLT_MAX, FLT_MAX};
1393 float max_axis[2] = {-FLT_MAX, -FLT_MAX};
1394
1395 do {
1396 BMEdge *e = static_cast<BMEdge *>(edge_links->link);
1397 BLI_assert(e->head.htype == BM_EDGE);
1398
1399 for (int j = 0; j < 2; j++) {
1400 BMVert *v_iter = (&e->v1)[j];
1401 BLI_assert(v_iter->head.htype == BM_VERT);
1402 /* ideally we could use 'v_iter->co[SORT_AXIS]' here,
1403 * but we need to sort the groups before setting the vertex array order */
1404 const float axis_value[2] = {
1405#if SORT_AXIS == 0
1406 dot_m3_v3_row_x(axis_mat, v_iter->co),
1407 dot_m3_v3_row_y(axis_mat, v_iter->co),
1408#else
1409 dot_m3_v3_row_y(axis_mat, v_iter->co),
1410 dot_m3_v3_row_x(axis_mat, v_iter->co),
1411#endif
1412 };
1413
1414 if (axis_pt_cmp(axis_value, min_axis) == -1) {
1415 g->vert_span.min = v_iter;
1416 copy_v2_v2(min_axis, axis_value);
1417 }
1418 if (axis_pt_cmp(axis_value, max_axis) == 1) {
1419 g->vert_span.max = v_iter;
1420 copy_v2_v2(max_axis, axis_value);
1421 }
1422 }
1423 } while ((edge_links = edge_links->next));
1424
1425 copy_v2_v2(g->vert_span.min_axis, min_axis);
1426 copy_v2_v2(g->vert_span.max_axis, max_axis);
1427
1428 g->has_prev_edge = false;
1429
1430 vert_arr_len += g->vert_len;
1431
1432 *(--group_arr_p) = g;
1433 }
1434 }
1435
1436 qsort(group_arr, group_arr_len, sizeof(*group_arr), group_min_cmp_fn);
1437
1438 /* we don't know how many unique verts there are connecting the edges, so over-alloc */
1439 vert_arr = static_cast<BMVert **>(
1440 BLI_memarena_alloc(mem_arena, sizeof(*vert_arr) * vert_arr_len));
1441 /* map vertex -> group index */
1442 verts_group_table = static_cast<uint *>(
1443 BLI_memarena_alloc(mem_arena, sizeof(*verts_group_table) * vert_arr_len));
1444
1445 vert_coords_backup = static_cast<float (*)[3]>(
1446 BLI_memarena_alloc(mem_arena, sizeof(*vert_coords_backup) * vert_arr_len));
1447
1448 {
1449 /* relative location, for higher precision calculations */
1450 const float f_co_ref[3] = {UNPACK3(BM_FACE_FIRST_LOOP(f)->v->co)};
1451
1452 int v_index = 0; /* global vert index */
1453 for (uint g_index = 0; g_index < group_arr_len; g_index++) {
1454 LinkNode *edge_links = static_cast<LinkNode *>(group_arr[g_index]->edge_links.link);
1455 do {
1456 BMEdge *e = static_cast<BMEdge *>(edge_links->link);
1457 for (int j = 0; j < 2; j++) {
1458 BMVert *v_iter = (&e->v1)[j];
1459 if (!BM_elem_flag_test(v_iter, VERT_IN_ARRAY)) {
1461
1462 /* not nice, but alternatives aren't much better :S */
1463 {
1464 copy_v3_v3(vert_coords_backup[v_index], v_iter->co);
1465
1466 /* for higher precision */
1467 sub_v3_v3(v_iter->co, f_co_ref);
1468
1469 float co_2d[2];
1470 mul_v2_m3v3(co_2d, axis_mat, v_iter->co);
1471 v_iter->co[0] = co_2d[0];
1472 v_iter->co[1] = co_2d[1];
1473 v_iter->co[2] = 0.0f;
1474 }
1475
1476 BM_elem_index_set(v_iter, v_index); /* set_dirty */
1477
1478 vert_arr[v_index] = v_iter;
1479 verts_group_table[v_index] = g_index;
1480 v_index++;
1481 }
1482 }
1483 } while ((edge_links = edge_links->next));
1484 }
1485 }
1486
1487 bm->elem_index_dirty |= BM_VERT;
1488
1489 /* Now create BVH tree.
1490 *
1491 * Note that a large epsilon is used because meshes with dimensions of around 100+ need it.
1492 * see #52329. */
1493 bvhtree = BLI_bvhtree_new(edge_arr_len, 1e-4f, 8, 8);
1494 for (uint i = 0; i < edge_arr_len; i++) {
1495 const float e_cos[2][3] = {
1496 {UNPACK2(edge_arr[i]->v1->co), 0.0f},
1497 {UNPACK2(edge_arr[i]->v2->co), 0.0f},
1498 };
1499 BLI_bvhtree_insert(bvhtree, i, (const float *)e_cos, 2);
1500 }
1501 BLI_bvhtree_balance(bvhtree);
1502
1503#ifdef USE_PARTIAL_CONNECT
1504 if (use_partial_connect) {
1505 /* needs to be done once the vertex indices have been written into */
1506 temp_vert_pairs.remap = static_cast<int *>(
1507 BLI_memarena_alloc(mem_arena, sizeof(*temp_vert_pairs.remap) * vert_arr_len));
1508 copy_vn_i(temp_vert_pairs.remap, vert_arr_len, -1);
1509
1510 TempVertPair *tvp = temp_vert_pairs.list;
1511 do {
1512 temp_vert_pairs.remap[BM_elem_index_get(tvp->v_temp)] = BM_elem_index_get(tvp->v_orig);
1513 } while ((tvp = tvp->next));
1514 }
1515#endif /* USE_PARTIAL_CONNECT */
1516
1517 /* Create connections between groups */
1518
1519 /* may be an over-alloc, but not by much */
1520 edge_net_new_len = uint(edge_net_init_len) + ((group_arr_len - 1) * 2);
1521 edge_net_new = static_cast<BMEdge **>(
1522 BLI_memarena_alloc(mem_arena, sizeof(*edge_net_new) * edge_net_new_len));
1523 memcpy(edge_net_new, edge_net_init, sizeof(*edge_net_new) * size_t(edge_net_init_len));
1524
1525 {
1526 uint edge_net_new_index = edge_net_init_len;
1527 /* start-end of the verts in the current group */
1528
1529 uint vert_range[2];
1530
1531 vert_range[0] = 0;
1532 vert_range[1] = group_arr[0]->vert_len;
1533
1535 args.bvhtree = bvhtree;
1536
1537 /* use the new edge array so we can scan edges which have been added */
1538 args.edge_arr = edge_arr;
1539 args.edge_arr_len = edge_arr_len;
1540
1541 /* we only want to check newly created edges */
1542 args.edge_arr_new = edge_net_new + edge_net_init_len;
1543 args.edge_arr_new_len = 0;
1544
1545 args.vert_range = vert_range;
1546
1547 for (uint g_index = 1; g_index < group_arr_len; g_index++) {
1548 EdgeGroupIsland *g = group_arr[g_index];
1549
1550 /* The range of verts this group uses in 'verts_arr' (not including the last index). */
1551 vert_range[0] = vert_range[1];
1552 vert_range[1] += g->vert_len;
1553
1554 if (g->has_prev_edge == false) {
1555 BMVert *v_origin = g->vert_span.min;
1556 /* Index of BMVert for the edge group connection with `v_origin`. */
1557 const int index_other = bm_face_split_edgenet_find_connection(&args, v_origin, false);
1558 // BLI_assert(index_other >= 0 && index_other < int(vert_arr_len));
1559
1560 /* only for degenerate geometry */
1561 if (index_other != -1) {
1562#ifdef USE_PARTIAL_CONNECT
1563 if ((use_partial_connect == false) ||
1565 temp_vert_pairs.remap, BM_elem_index_get(v_origin), index_other) == false))
1566#endif
1567 {
1568 BMVert *v_end = vert_arr[index_other];
1569
1570 edge_net_new[edge_net_new_index] = BM_edge_create(
1571 bm, v_origin, v_end, nullptr, eBMCreateFlag(0));
1572#ifdef USE_PARTIAL_CONNECT
1573 BM_elem_index_set(edge_net_new[edge_net_new_index], edge_net_new_index);
1574#endif
1575 edge_net_new_index++;
1576 args.edge_arr_new_len++;
1577 }
1578 }
1579 }
1580
1581 {
1582 BMVert *v_origin = g->vert_span.max;
1583 /* Index of BMVert for the edge group connection with `v_origin`. */
1584 const int index_other = bm_face_split_edgenet_find_connection(&args, v_origin, true);
1585 // BLI_assert(index_other >= 0 && index_other < int(vert_arr_len));
1586
1587 /* only for degenerate geometry */
1588 if (index_other != -1) {
1589#ifdef USE_PARTIAL_CONNECT
1590 if ((use_partial_connect == false) ||
1592 temp_vert_pairs.remap, BM_elem_index_get(v_origin), index_other) == false))
1593#endif
1594 {
1595 BMVert *v_end = vert_arr[index_other];
1596 edge_net_new[edge_net_new_index] = BM_edge_create(
1597 bm, v_origin, v_end, nullptr, eBMCreateFlag(0));
1598#ifdef USE_PARTIAL_CONNECT
1599 BM_elem_index_set(edge_net_new[edge_net_new_index], edge_net_new_index);
1600#endif
1601 edge_net_new_index++;
1602 args.edge_arr_new_len++;
1603 }
1604
1605 /* tell the 'next' group it doesn't need to create its own back-link */
1606 uint g_index_other = verts_group_table[index_other];
1607 group_arr[g_index_other]->has_prev_edge = true;
1608 }
1609 }
1610 }
1611 BLI_assert(edge_net_new_len >= edge_net_new_index);
1612 edge_net_new_len = edge_net_new_index;
1613 }
1614
1615 BLI_bvhtree_free(bvhtree);
1616
1617 *r_edge_net_new = edge_net_new;
1618 *r_edge_net_new_len = edge_net_new_len;
1619 ok = true;
1620
1621 for (uint i = 0; i < vert_arr_len; i++) {
1622 copy_v3_v3(vert_arr[i]->co, vert_coords_backup[i]);
1623 }
1624
1625finally:
1626
1627#ifdef USE_PARTIAL_CONNECT
1628 /* don't free 'vert_temp_pair_list', its part of the arena */
1629 if (use_partial_connect) {
1630
1631/* Sanity check: ensure we don't have connecting edges before splicing begins. */
1632# ifndef NDEBUG
1633 {
1634 TempVertPair *tvp = temp_vert_pairs.list;
1635 do {
1636 /* We must _never_ create connections here
1637 * (in case the islands can't have a connection at all). */
1638 BLI_assert(BM_edge_exists(tvp->v_orig, tvp->v_temp) == nullptr);
1639 } while ((tvp = tvp->next));
1640 }
1641# endif
1642
1643 TempVertPair *tvp = temp_vert_pairs.list;
1644 do {
1645 /* its _very_ unlikely the edge exists,
1646 * however splicing may cause this. see: #48012 */
1647 if (!BM_edge_exists(tvp->v_orig, tvp->v_temp)) {
1648 BM_vert_splice(bm, tvp->v_orig, tvp->v_temp);
1649 }
1650 } while ((tvp = tvp->next));
1651
1652 /* Remove edges which have become doubles since splicing vertices together,
1653 * its less trouble than detecting future-doubles on edge-creation. */
1654 for (uint i = edge_net_init_len; i < edge_net_new_len; i++) {
1655 while (BM_edge_find_double(edge_net_new[i])) {
1656 BM_edge_kill(bm, edge_net_new[i]);
1657 edge_net_new_len--;
1658 if (i == edge_net_new_len) {
1659 break;
1660 }
1661 edge_net_new[i] = edge_net_new[edge_net_new_len];
1662 }
1663 }
1664
1665 *r_edge_net_new_len = edge_net_new_len;
1666 }
1667#endif
1668
1669 for (uint i = 0; i < edge_arr_len; i++) {
1673 }
1674
1675#undef VERT_IN_ARRAY
1676#undef VERT_NOT_IN_STACK
1677#undef EDGE_NOT_IN_STACK
1678
1679 return ok;
1680}
1681
1682#undef SORT_AXIS
1683
CustomData interface, see also DNA_customdata_types.h.
void CustomData_bmesh_copy_block(CustomData &data, void *src_block, void **dst_block)
bool CustomData_has_math(const CustomData *data)
void CustomData_bmesh_interp(CustomData *data, const void **src_blocks, const float *weights, int count, void *dst_block)
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:18
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE
MINLINE axis_t min_axis(axis_t a, axis_t b)
BVHTree * BLI_bvhtree_new(int maxsize, float epsilon, char tree_type, char axis)
#define BVH_RAYCAST_DIST_MAX
void BLI_bvhtree_balance(BVHTree *tree)
void BLI_bvhtree_free(BVHTree *tree)
void BLI_bvhtree_insert(BVHTree *tree, int index, const float co[3], int numpoints)
int BLI_bvhtree_ray_cast_ex(const BVHTree *tree, const float co[3], const float dir[3], float radius, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata, int flag)
#define BLI_ASSERT_UNIT_V2(v)
int isect_seg_seg_v2_point_ex(const float v0[2], const float v1[2], const float v2[2], const float v3[2], float endpoint_bias, float r_vi[2])
bool isect_ray_seg_v2(const float ray_origin[2], const float ray_direction[2], const float v0[2], const float v1[2], float *r_lambda, float *r_u)
float line_point_factor_v2(const float p[2], const float l1[2], const float l2[2])
void axis_dominant_v3_to_m3(float r_mat[3][3], const float normal[3])
Normal to x,y matrix.
void interp_weights_poly_v2(float w[], float v[][2], int n, const float co[2])
void mul_v2_m3v3(float r[2], const float M[3][3], const float a[3])
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3(float r[3], const float a[3])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE float dot_m3_v3_row_x(const float M[3][3], const float a[3]) ATTR_WARN_UNUSED_RESULT
float angle_signed_on_axis_v3v3v3_v3(const float v1[3], const float v2[3], const float v3[3], const float axis[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE float dot_m3_v3_row_y(const float M[3][3], const float a[3]) ATTR_WARN_UNUSED_RESULT
void copy_vn_i(int *array_tar, int size, int val)
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float dot_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v2(float n[2])
void * BLI_memarena_alloc(MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_ALLOC_SIZE(2)
int BLI_sortutil_cmp_float_reverse(const void *a_, const void *b_)
Definition sort_utils.cc:39
unsigned int uint
#define UNPACK2(a)
#define ARRAY_SET_ITEMS(...)
#define UNUSED_VARS_NDEBUG(...)
#define UNPACK3(a)
#define UNLIKELY(x)
#define LIKELY(x)
#define STACK_POP(stack)
#define STACK_POP_PTR(stack)
#define STACK_PUSH(stack, val)
#define STACK_DECLARE(stack)
#define STACK_SIZE(stack)
#define STACK_PUSH_RET_PTR(stack)
#define STACK_INIT(stack, stack_num)
Read Guarded memory(de)allocation.
#define BM_DISK_EDGE_NEXT(e, v)
#define BM_FACE_FIRST_LOOP(p)
@ BM_ELEM_SELECT
@ BM_ELEM_INTERNAL_TAG
void bmesh_face_swap_data(BMFace *f_a, BMFace *f_b)
bool BM_vert_splice(BMesh *bm, BMVert *v_dst, BMVert *v_src)
Splice Vert.
void BM_vert_kill(BMesh *bm, BMVert *v)
void BM_face_kill(BMesh *bm, BMFace *f)
BMFace * BM_face_create_verts(BMesh *bm, BMVert **vert_arr, const int len, const BMFace *f_example, const eBMCreateFlag create_flag, const bool create_edges)
void BM_edge_kill(BMesh *bm, BMEdge *e)
BMVert * BM_vert_create(BMesh *bm, const float co[3], const BMVert *v_example, const eBMCreateFlag create_flag)
Main function for creating a new vertex.
Definition bmesh_core.cc:41
void BM_vert_separate_tested_edges(BMesh *, BMVert *v_dst, BMVert *v_src, bool(*testfn)(BMEdge *, void *arg), void *arg)
#define VERT_VISIT
BMEdge * BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *e_example, const eBMCreateFlag create_flag)
Main function for creating a new edge.
eBMCreateFlag
Definition bmesh_core.hh:27
@ BM_CREATE_NOP
Definition bmesh_core.hh:28
#define BM_elem_index_get(ele)
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_index_set(ele, index)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
#define BM_ITER_ELEM(ele, iter, data, itype)
@ BM_VERTS_OF_EDGE
@ BM_LOOPS_OF_VERT
BMesh const char void * data
BMesh * bm
return true
#define BM_EDGE
#define BM_VERT
static BMVert * bm_face_split_edgenet_partial_connect(BMesh *bm, BMVert *v_delimit, BMFace *f)
static BMLoop * bm_edge_flagged_radial_first(BMEdge *e)
static bool bm_face_split_edgenet_find_loop_pair(BMVert *v_init, const float face_normal[3], const float face_normal_matrix[3][3], BMEdge *e_pair[2])
static int bm_face_split_edgenet_find_connection(const EdgeGroup_FindConnection_Args *args, BMVert *v_origin, bool direction_sign)
static BMEdge * test_edges_isect_2d_ray(const EdgeGroup_FindConnection_Args *args, BMVert *v_origin, const float dir[3])
static bool bm_face_split_edgenet_find_loop_walk(BMVert *v_init, const float face_normal[3], VertOrder *edge_order, const uint edge_order_len, BMEdge *e_pair[2])
#define EDGE_NET
static bool bm_vert_partial_connect_check_overlap(const int *remap, const int v_a_index, const int v_b_index)
#define VERT_NOT_IN_STACK
bool BM_face_split_edgenet_connect_islands(BMesh *bm, BMFace *f, BMEdge **edge_net_init, const uint edge_net_init_len, bool use_partial_connect, MemArena *mem_arena, BMEdge ***r_edge_net_new, uint *r_edge_net_new_len)
bool BM_face_split_edgenet(BMesh *bm, BMFace *f, BMEdge **edge_net, const int edge_net_len, blender::Vector< BMFace * > *r_face_arr)
static void bvhtree_test_edges_isect_2d_ray_cb(void *user_data, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
static bool bm_face_split_edgenet_find_loop(BMVert *v_init, const float face_normal[3], const float face_normal_matrix[3][3], VertOrder *edge_order, const uint edge_order_len, BMVert **r_face_verts, int *r_face_verts_len)
#define VERT_IS_VALID
static BMEdge * test_edges_isect_2d_vert(const EdgeGroup_FindConnection_Args *args, BMVert *v_origin, BMVert *v_other)
#define SORT_AXIS
static void bvhtree_test_edges_isect_2d_vert_cb(void *user_data, int index, const BVHTreeRay *, BVHTreeRayHit *hit)
#define EDGE_NOT_IN_STACK
#define VERT_IN_QUEUE
#define FOREACH_VERT_EDGE(v_, e_, body_)
#define FACE_NET
static void normalize_v2_m3_v3v3(float out[2], const float axis_mat[3][3], const float v1[3], const float v2[3])
static bool bm_face_split_edgenet_find_loop_pair_exists(BMVert *v_init)
static bool test_tagged_and_notface(BMEdge *e, void *fptr)
#define VERT_IN_ARRAY
BLI_INLINE bool edge_isect_verts_point_2d(const BMEdge *e, const BMVert *v_a, const BMVert *v_b, float r_isect[2])
static uint bm_edge_flagged_radial_count(BMEdge *e)
static int group_min_cmp_fn(const void *p1, const void *p2)
BLI_INLINE int axis_pt_cmp(const float pt_a[2], const float pt_b[2])
#define BM_ELEM_API_FLAG_DISABLE(element, f)
#define BM_ELEM_API_FLAG_TEST(element, f)
#define BM_ELEM_API_FLAG_ENABLE(element, f)
bool BM_edge_in_face(const BMEdge *e, const BMFace *f)
BMEdge * BM_edge_find_double(BMEdge *e)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
BLI_INLINE BMVert * BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
void append(const T &value)
bool is_empty() const
void clear_and_shrink()
nullptr float
#define out
#define printf(...)
int count
static MemArena * mem_arena
Definition makesdna.cc:62
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
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
#define swap(a, b)
Definition sort.cc:59
#define FLT_MAX
Definition stdcycles.h:14
BMVert * v1
BMVert * v2
BMHeader head
float no[3]
void * data
BMHeader head
struct BMVert * v
struct BMEdge * e
struct BMFace * f
struct BMLoop * next
float co[3]
struct BMEdge * e
BMHeader head
float direction[3]
struct EdgeGroupIsland::@103042325240010213160232253114225104227144076314 vert_span
void * link
struct LinkNode * next
i
Definition text_draw.cc:230
uint len