Blender V4.3
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.h"
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/* -------------------------------------------------------------------- */
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);
94 normalize_v2(out);
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 = static_cast<VertOrder *>(
478 MEM_mallocN(sizeof(*edge_order) * edge_order_len, __func__));
479
480 /* use later */
481 face_verts = static_cast<BMVert **>(
482 MEM_mallocN(sizeof(*face_verts) * (edge_net_len + f->len), __func__));
483
484 vert_queue = static_cast<BMVert **>(
485 MEM_mallocN(sizeof(vert_queue) * (edge_net_len + f->len), __func__));
486 STACK_INIT(vert_queue, f->len + edge_net_len);
487
490
491#ifndef NDEBUG
492 for (i = 0; i < edge_net_len; i++) {
493 BLI_assert(BM_ELEM_API_FLAG_TEST(edge_net[i], EDGE_NET) == 0);
494 BLI_assert(BM_edge_in_face(edge_net[i], f) == false);
495 }
496 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
497 do {
499 } while ((l_iter = l_iter->next) != l_first);
500#endif
501
502 /* NOTE: 'VERT_IN_QUEUE' is often not needed at all,
503 * however in rare cases verts are added multiple times to the queue,
504 * that on its own is harmless but in _very_ rare cases,
505 * the queue will overflow its maximum size,
506 * so we better be strict about this! see: #51539 */
507
508 for (i = 0; i < edge_net_len; i++) {
509 BM_ELEM_API_FLAG_ENABLE(edge_net[i], EDGE_NET);
510 BM_ELEM_API_FLAG_DISABLE(edge_net[i]->v1, VERT_IN_QUEUE);
512 }
513 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
514 do {
517 } while ((l_iter = l_iter->next) != l_first);
518
519 float face_normal_matrix[3][3];
520 axis_dominant_v3_to_m3(face_normal_matrix, f->no);
521
522 /* any vert can be used to begin with */
523 STACK_PUSH(vert_queue, l_first->v);
525
527 while ((v = STACK_POP(vert_queue))) {
530 v, f->no, face_normal_matrix, edge_order, edge_order_len, face_verts, &face_verts_len))
531 {
532 BMFace *f_new;
533
534 f_new = BM_face_create_verts(bm, face_verts, face_verts_len, f, BM_CREATE_NOP, false);
535
536 for (i = 0; i < edge_net_len; i++) {
538 }
539
540 if (f_new) {
541 face_arr.append(f_new);
542 copy_v3_v3(f_new->no, f->no);
543
544 /* warning, normally don't do this,
545 * its needed for mesh intersection - which tracks face-sides based on selection */
546 f_new->head.hflag = f->head.hflag;
547 if (f->head.hflag & BM_ELEM_SELECT) {
548 bm->totfacesel++;
549 }
550
552
553 /* add new verts to keep finding loops for
554 * (verts between boundary and manifold edges) */
555 l_iter = l_first = BM_FACE_FIRST_LOOP(f_new);
556 do {
557 /* Avoid adding to queue multiple times (not common but happens). */
558 if (!BM_ELEM_API_FLAG_TEST(l_iter->v, VERT_IN_QUEUE) &&
560 {
561 STACK_PUSH(vert_queue, l_iter->v);
563 }
564 } while ((l_iter = l_iter->next) != l_first);
565 }
566 }
567 }
568
570 /* reuse VERT_VISIT here to tag vert's already interpolated */
571 BMIter iter;
572 BMLoop *l_other;
573
574 /* See: #BM_loop_interp_from_face for similar logic. */
575 void **blocks = BLI_array_alloca(blocks, f->len);
576 float(*cos_2d)[2] = BLI_array_alloca(cos_2d, f->len);
577 float *w = BLI_array_alloca(w, f->len);
578 float axis_mat[3][3];
579 float co[2];
580
581 /* interior loops */
582 axis_dominant_v3_to_m3(axis_mat, f->no);
583
584 /* first simply copy from existing face */
585 i = 0;
586 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
587 do {
588 BM_ITER_ELEM (l_other, &iter, l_iter->v, BM_LOOPS_OF_VERT) {
589 if ((l_other->f != f) && BM_ELEM_API_FLAG_TEST(l_other->f, FACE_NET)) {
590 CustomData_bmesh_copy_block(bm->ldata, l_iter->head.data, &l_other->head.data);
591 }
592 }
593 /* tag not to interpolate */
595
596 mul_v2_m3v3(cos_2d[i], axis_mat, l_iter->v->co);
597 blocks[i] = l_iter->head.data;
598
599 } while ((void)i++, (l_iter = l_iter->next) != l_first);
600
601 for (i = 0; i < edge_net_len; i++) {
602 BM_ITER_ELEM (v, &iter, edge_net[i], BM_VERTS_OF_EDGE) {
604 BMIter liter;
605
607
608 /* interpolate this loop, then copy to the rest */
609 l_first = nullptr;
610
611 BM_ITER_ELEM (l_iter, &liter, v, BM_LOOPS_OF_VERT) {
612 if (BM_ELEM_API_FLAG_TEST(l_iter->f, FACE_NET)) {
613 if (l_first == nullptr) {
614 mul_v2_m3v3(co, axis_mat, v->co);
615 interp_weights_poly_v2(w, cos_2d, f->len, co);
617 &bm->ldata, (const void **)blocks, w, nullptr, f->len, l_iter->head.data);
618 l_first = l_iter;
619 }
620 else {
621 CustomData_bmesh_copy_block(bm->ldata, l_first->head.data, &l_iter->head.data);
622 }
623 }
624 }
625 }
626 }
627 }
628 }
629
630 /* cleanup */
631 for (i = 0; i < edge_net_len; i++) {
633 /* from interp only */
634 BM_ELEM_API_FLAG_DISABLE(edge_net[i]->v1, VERT_VISIT);
636 }
637 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
638 do {
640 /* from interp only */
642 } while ((l_iter = l_iter->next) != l_first);
643
644 if (!face_arr.is_empty()) {
645 bmesh_face_swap_data(f, face_arr[0]);
646 BM_face_kill(bm, face_arr[0]);
647 face_arr[0] = f;
648 }
649 else {
651 }
652
653 for (BMFace *face : face_arr) {
655 }
656
657 if (r_face_arr) {
658 *r_face_arr = std::move(face_arr);
659 }
660
661 MEM_freeN(edge_order);
662 MEM_freeN(face_verts);
663 MEM_freeN(vert_queue);
664
665 return true;
666}
667
668#undef FACE_NET
669#undef VERT_VISIT
670#undef EDGE_NET
671
674/* -------------------------------------------------------------------- */
688#define USE_PARTIAL_CONNECT
689
690#define VERT_IS_VALID BM_ELEM_INTERNAL_TAG
691
692/* can be X or Y */
693#define SORT_AXIS 0
694
696 const BMVert *v_a,
697 const BMVert *v_b,
698 float r_isect[2])
699{
700 /* This bias seems like it could be too large,
701 * mostly its not needed, see #52329 for example where it is. */
702 const float endpoint_bias = 1e-4f;
704 v_a->co, v_b->co, e->v1->co, e->v2->co, endpoint_bias, r_isect) == 1) &&
705 ((e->v1 != v_a) && (e->v2 != v_a) && (e->v1 != v_b) && (e->v2 != v_b)));
706}
707
708BLI_INLINE int axis_pt_cmp(const float pt_a[2], const float pt_b[2])
709{
710 if (pt_a[0] < pt_b[0]) {
711 return -1;
712 }
713 if (pt_a[0] > pt_b[0]) {
714 return 1;
715 }
716 if (pt_a[1] < pt_b[1]) {
717 return -1;
718 }
719 if (pt_a[1] > pt_b[1]) {
720 return 1;
721 }
722 return 0;
723}
724
731 LinkNode edge_links; /* keep first */
733
734 /* Set the following vars once we have >1 groups */
735
736 /* when an edge in a previous group connects to this one,
737 * so there's no need to create one pointing back. */
739
740 /* verts in the group which has the lowest & highest values,
741 * the lower vertex is connected to the first edge */
742 struct {
744 /* used for sorting only */
745 float min_axis[2];
746 float max_axis[2];
748};
749
750static int group_min_cmp_fn(const void *p1, const void *p2)
751{
752 const EdgeGroupIsland *g1 = *(EdgeGroupIsland **)p1;
753 const EdgeGroupIsland *g2 = *(EdgeGroupIsland **)p2;
754 /* min->co[SORT_AXIS] hasn't been applied yet */
755 int test = axis_pt_cmp(g1->vert_span.min_axis, g2->vert_span.min_axis);
756 if (UNLIKELY(test == 0)) {
758 }
759 return test;
760}
761
771
779
780static void bvhtree_test_edges_isect_2d_vert_cb(void *user_data,
781 int index,
782 const BVHTreeRay * /*ray*/,
783 BVHTreeRayHit *hit)
784{
785 Edges_VertVert_BVHTreeTest *data = static_cast<Edges_VertVert_BVHTreeTest *>(user_data);
786 const BMEdge *e = data->edge_arr[index];
787 const int v1_index = BM_elem_index_get(e->v1);
788 float co_isect[2];
789
790 if (edge_isect_verts_point_2d(e, data->v_origin, data->v_other, co_isect)) {
791 const float t = line_point_factor_v2(co_isect, data->v_origin->co, data->v_other->co);
792 const float dist_new = data->dist_orig * t;
793 /* avoid float precision issues, possible this is greater,
794 * check above zero to allow some overlap
795 * (and needed for partial-connect which will overlap vertices) */
796 if (LIKELY((dist_new < hit->dist) && (dist_new > 0.0f))) {
797 /* v1/v2 will both be in the same group */
798 if (v1_index < int(data->vert_range[0]) || v1_index >= int(data->vert_range[1])) {
799 hit->dist = dist_new;
800 hit->index = index;
801 }
802 }
803 }
804}
805
806static void bvhtree_test_edges_isect_2d_ray_cb(void *user_data,
807 int index,
808 const BVHTreeRay *ray,
809 BVHTreeRayHit *hit)
810{
811 Edges_VertRay_BVHTreeTest *data = static_cast<Edges_VertRay_BVHTreeTest *>(user_data);
812 const BMEdge *e = data->edge_arr[index];
813
814 /* direction is normalized, so this will be the distance */
815 float dist_new;
817 data->v_origin->co, ray->direction, e->v1->co, e->v2->co, &dist_new, nullptr))
818 {
819 /* avoid float precision issues, possible this is greater,
820 * check above zero to allow some overlap
821 * (and needed for partial-connect which will overlap vertices) */
822 if (LIKELY(dist_new < hit->dist && (dist_new > 0.0f))) {
823 if (e->v1 != data->v_origin && e->v2 != data->v_origin) {
824 const int v1_index = BM_elem_index_get(e->v1);
825 /* v1/v2 will both be in the same group */
826 if (v1_index < int(data->vert_range[0]) || v1_index >= int(data->vert_range[1])) {
827 hit->dist = dist_new;
828 hit->index = index;
829 }
830 }
831 }
832 }
833}
834
851
853 BMVert *v_origin,
854 BMVert *v_other)
855{
856 int index;
857
858 BVHTreeRayHit hit = {0};
859 float dir[3];
860
861 sub_v2_v2v2(dir, v_other->co, v_origin->co);
862 dir[2] = 0.0f;
863 hit.index = -1;
864 hit.dist = normalize_v2(dir);
865
866 Edges_VertVert_BVHTreeTest user_data = {0};
867 user_data.dist_orig = hit.dist;
868 user_data.edge_arr = args->edge_arr;
869 user_data.v_origin = v_origin;
870 user_data.v_other = v_other;
871 user_data.vert_range = args->vert_range;
872
873 index = BLI_bvhtree_ray_cast_ex(args->bvhtree,
874 v_origin->co,
875 dir,
876 0.0f,
877 &hit,
879 &user_data,
880 0);
881
882 BMEdge *e_hit = (index != -1) ? args->edge_arr[index] : nullptr;
883
884 /* check existing connections (no spatial optimization here since we're continually adding). */
885 if (LIKELY(index == -1)) {
886 float t_best = 1.0f;
887 for (uint i = 0; i < args->edge_arr_new_len; i++) {
888 float co_isect[2];
889 if (UNLIKELY(edge_isect_verts_point_2d(args->edge_arr_new[i], v_origin, v_other, co_isect)))
890 {
891 const float t_test = line_point_factor_v2(co_isect, v_origin->co, v_other->co);
892 if (t_test < t_best) {
893 t_best = t_test;
894
895 e_hit = args->edge_arr_new[i];
896 }
897 }
898 }
899 }
900
901 return e_hit;
902}
903
909 BMVert *v_origin,
910 const float dir[3])
911{
912 int index;
913 BVHTreeRayHit hit = {0};
914
916
917 hit.index = -1;
918 hit.dist = BVH_RAYCAST_DIST_MAX;
919
920 Edges_VertRay_BVHTreeTest user_data = {nullptr};
921 user_data.edge_arr = args->edge_arr;
922 user_data.v_origin = v_origin;
923 user_data.vert_range = args->vert_range;
924
925 index = BLI_bvhtree_ray_cast_ex(args->bvhtree,
926 v_origin->co,
927 dir,
928 0.0f,
929 &hit,
931 &user_data,
932 0);
933
934 BMEdge *e_hit = (index != -1) ? args->edge_arr[index] : nullptr;
935
936 /* check existing connections (no spatial optimization here since we're continually adding). */
937 if (LIKELY(index != -1)) {
938 for (uint i = 0; i < args->edge_arr_new_len; i++) {
939 BMEdge *e = args->edge_arr_new[i];
940 float dist_new;
941 if (isect_ray_seg_v2(v_origin->co, dir, e->v1->co, e->v2->co, &dist_new, nullptr)) {
942 if (e->v1 != v_origin && e->v2 != v_origin) {
943 /* avoid float precision issues, possible this is greater */
944 if (LIKELY(dist_new < hit.dist)) {
945 hit.dist = dist_new;
946
947 e_hit = args->edge_arr_new[i];
948 }
949 }
950 }
951 }
952 }
953
954 return e_hit;
955}
956
958 BMVert *v_origin,
959 /* false = negative, true = positive */
960 bool direction_sign)
961{
976 float dir[3] = {0, 0, 0};
977 dir[SORT_AXIS] = direction_sign ? 1.0f : -1.0f;
978 BMEdge *e_hit = test_edges_isect_2d_ray(args, v_origin, dir);
979 BMVert *v_other = nullptr;
980
981 if (e_hit) {
982 BMVert *v_other_fallback = nullptr;
983
984 BLI_SMALLSTACK_DECLARE(vert_search, BMVert *);
985
986 /* ensure we never add verts multiple times (not all that likely - but possible) */
987 BLI_SMALLSTACK_DECLARE(vert_blacklist, BMVert *);
988
989 do {
990 BMVert *v_pair[2];
991 /* ensure the closest vertex is popped back off the stack first */
992 if (len_squared_v2v2(v_origin->co, e_hit->v1->co) >
993 len_squared_v2v2(v_origin->co, e_hit->v2->co))
994 {
995 ARRAY_SET_ITEMS(v_pair, e_hit->v1, e_hit->v2);
996 }
997 else {
998 ARRAY_SET_ITEMS(v_pair, e_hit->v2, e_hit->v1);
999 }
1000
1001 for (int j = 0; j < 2; j++) {
1002 BMVert *v_iter = v_pair[j];
1003 if (BM_elem_flag_test(v_iter, VERT_IS_VALID)) {
1004 if (direction_sign ? (v_iter->co[SORT_AXIS] > v_origin->co[SORT_AXIS]) :
1005 (v_iter->co[SORT_AXIS] < v_origin->co[SORT_AXIS]))
1006 {
1007 BLI_SMALLSTACK_PUSH(vert_search, v_iter);
1008 BLI_SMALLSTACK_PUSH(vert_blacklist, v_iter);
1010 }
1011 }
1012 }
1013 v_other_fallback = v_other;
1014
1015 } while ((v_other = static_cast<BMVert *>(BLI_SMALLSTACK_POP(vert_search))) &&
1016 (e_hit = test_edges_isect_2d_vert(args, v_origin, v_other)));
1017
1018 if (v_other == nullptr) {
1019 printf("Using fallback\n");
1020 v_other = v_other_fallback;
1021 }
1022
1023 /* reset the blacklist flag, for future use */
1024 BMVert *v;
1025 while ((v = static_cast<BMVert *>(BLI_SMALLSTACK_POP(vert_blacklist)))) {
1027 }
1028 }
1029
1030 /* if we reach this line, v_other is either the best vertex or its nullptr */
1031 return v_other ? BM_elem_index_get(v_other) : -1;
1032}
1033
1038#ifdef USE_PARTIAL_CONNECT
1039
1044static bool test_tagged_and_notface(BMEdge *e, void *fptr)
1045{
1046 BMFace *f = (BMFace *)fptr;
1048}
1049
1058{
1059 /* -------------------------------------------------------------------- */
1060 /* Initial check that we may be a delimiting vert (keep this fast) */
1061
1062 /* initial check - see if we have 3+ flagged edges attached to 'v_delimit'
1063 * if not, we can early exit */
1064 LinkNode *e_delimit_list = nullptr;
1065 uint e_delimit_list_len = 0;
1066
1067# define EDGE_NOT_IN_STACK BM_ELEM_INTERNAL_TAG
1068# define VERT_NOT_IN_STACK BM_ELEM_INTERNAL_TAG
1069
1070# define FOREACH_VERT_EDGE(v_, e_, body_) \
1071 { \
1072 BMEdge *e_ = v_->e; \
1073 do { \
1074 body_ \
1075 } while ((e_ = BM_DISK_EDGE_NEXT(e_, v_)) != v_->e); \
1076 } \
1077 ((void)0)
1078
1079 /* start with face edges, since we need to split away wire-only edges */
1080 BMEdge *e_face_init = nullptr;
1081
1082 FOREACH_VERT_EDGE(v_delimit, e_iter, {
1083 if (BM_elem_flag_test(e_iter, EDGE_NOT_IN_STACK)) {
1085 BLI_linklist_prepend_alloca(&e_delimit_list, e_iter);
1086 e_delimit_list_len++;
1087 if (e_iter->l != nullptr && BM_edge_in_face(e_iter, f)) {
1088 e_face_init = e_iter;
1089 }
1090 }
1091 });
1092
1093 /* skip typical edge-chain verts */
1094 if (LIKELY(e_delimit_list_len <= 2)) {
1095 return nullptr;
1096 }
1097
1098 /* -------------------------------------------------------------------- */
1099 /* Complicated stuff starts now! */
1100
1101 /* Store connected vertices for restoring the flag */
1102 LinkNode *vert_stack = nullptr;
1103 BLI_linklist_prepend_alloca(&vert_stack, v_delimit);
1105
1106 /* Walk the net... */
1107 {
1108 BLI_SMALLSTACK_DECLARE(search, BMVert *);
1109 BMVert *v_other = BM_edge_other_vert(e_face_init ? e_face_init : v_delimit->e, v_delimit);
1110
1111 BLI_SMALLSTACK_PUSH(search, v_other);
1112 if (BM_elem_flag_test(v_other, VERT_NOT_IN_STACK)) {
1114 BLI_linklist_prepend_alloca(&vert_stack, v_other);
1115 }
1116
1117 while ((v_other = static_cast<BMVert *>(BLI_SMALLSTACK_POP(search)))) {
1119 BMEdge *e_iter = v_other->e;
1120 do {
1121 BMVert *v_step = BM_edge_other_vert(e_iter, v_other);
1122 if (BM_elem_flag_test(e_iter, EDGE_NOT_IN_STACK)) {
1123 if (BM_elem_flag_test(v_step, VERT_NOT_IN_STACK)) {
1125 BLI_SMALLSTACK_PUSH(search, v_step);
1126 BLI_linklist_prepend_alloca(&vert_stack, v_step);
1127 }
1128 }
1129 } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v_other)) != v_other->e);
1130 }
1131 }
1132
1133 /* Detect if this is a delimiter
1134 * by checking if we didn't walk any of edges connected to 'v_delimit'. */
1135 bool is_delimit = false;
1136 FOREACH_VERT_EDGE(v_delimit, e_iter, {
1137 BMVert *v_step = BM_edge_other_vert(e_iter, v_delimit);
1138 if (BM_elem_flag_test(v_step, VERT_NOT_IN_STACK) && !BM_edge_in_face(e_iter, f)) {
1139 is_delimit = true; /* if one vertex is valid - we have a mix */
1140 }
1141 else {
1142 /* match the vertex flag (only for edges around 'v_delimit') */
1144 }
1145 });
1146
1147# undef FOREACH_VERT_EDGE
1148
1149 /* Execute the split */
1150 BMVert *v_split = nullptr;
1151 if (is_delimit) {
1152 v_split = BM_vert_create(bm, v_delimit->co, nullptr, eBMCreateFlag(0));
1155
1156 BLI_assert(v_delimit->e != nullptr);
1157
1158/* Degenerate, avoid eternal loop, see: #59074. */
1159# if 0
1160 BLI_assert(v_split->e != nullptr);
1161# else
1162 if (UNLIKELY(v_split->e == nullptr)) {
1163 BM_vert_kill(bm, v_split);
1164 v_split = nullptr;
1165 }
1166# endif
1167 }
1168
1169 /* Restore flags */
1170 do {
1172 } while ((vert_stack = vert_stack->next));
1173
1174 do {
1175 BM_elem_flag_enable((BMEdge *)e_delimit_list->link, EDGE_NOT_IN_STACK);
1176 } while ((e_delimit_list = e_delimit_list->next));
1177
1178# undef EDGE_NOT_IN_STACK
1179# undef VERT_NOT_IN_STACK
1180
1181 return v_split;
1182}
1183
1187static bool bm_vert_partial_connect_check_overlap(const int *remap,
1188 const int v_a_index,
1189 const int v_b_index)
1190{
1191 /* Connected to each other. */
1192 if (UNLIKELY((remap[v_a_index] == v_b_index) || (remap[v_b_index] == v_a_index))) {
1193 return true;
1194 }
1195 return false;
1196}
1197
1198#endif /* USE_PARTIAL_CONNECT */
1199
1201 BMFace *f,
1202 BMEdge **edge_net_init,
1203 const uint edge_net_init_len,
1204 bool use_partial_connect,
1206 BMEdge ***r_edge_net_new,
1207 uint *r_edge_net_new_len)
1208{
1209 /* -------------------------------------------------------------------- */
1222 const uint edge_arr_len = uint(edge_net_init_len) + uint(f->len);
1223 BMEdge **edge_arr = static_cast<BMEdge **>(
1224 BLI_memarena_alloc(mem_arena, sizeof(*edge_arr) * edge_arr_len));
1225 bool ok = false;
1226 uint edge_net_new_len = uint(edge_net_init_len);
1227
1228 memcpy(edge_arr, edge_net_init, sizeof(*edge_arr) * size_t(edge_net_init_len));
1229
1230/* _must_ clear on exit */
1231#define EDGE_NOT_IN_STACK BM_ELEM_INTERNAL_TAG
1232#define VERT_NOT_IN_STACK BM_ELEM_INTERNAL_TAG
1233
1234 {
1235 uint i = edge_net_init_len;
1236 BMLoop *l_iter, *l_first;
1237 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1238 do {
1241 edge_arr[i++] = l_iter->e;
1242 } while ((l_iter = l_iter->next) != l_first);
1243 BLI_assert(i == edge_arr_len);
1244 }
1245
1246 for (uint i = 0; i < edge_arr_len; i++) {
1248 BM_elem_flag_enable(edge_arr[i]->v1, VERT_NOT_IN_STACK);
1250 }
1251
1252#ifdef USE_PARTIAL_CONNECT
1253 /* Split-out delimiting vertices */
1254 struct TempVertPair {
1255 TempVertPair *next;
1256 BMVert *v_temp;
1257 BMVert *v_orig;
1258 };
1259
1260 struct {
1261 TempVertPair *list;
1262 uint len;
1263 int *remap; /* temp -> orig mapping */
1264 } temp_vert_pairs = {nullptr};
1265
1266 if (use_partial_connect) {
1267 for (uint i = 0; i < edge_net_init_len; i++) {
1268 for (uint j = 0; j < 2; j++) {
1269 BMVert *v_delimit = (&edge_arr[i]->v1)[j];
1270 BMVert *v_other;
1271
1272 /* NOTE: remapping will _never_ map a vertex to an already mapped vertex. */
1273 while (UNLIKELY(v_other = bm_face_split_edgenet_partial_connect(bm, v_delimit, f))) {
1274 TempVertPair *tvp = static_cast<TempVertPair *>(
1275 BLI_memarena_alloc(mem_arena, sizeof(*tvp)));
1276 tvp->next = temp_vert_pairs.list;
1277 tvp->v_orig = v_delimit;
1278 tvp->v_temp = v_other;
1279 temp_vert_pairs.list = tvp;
1280 temp_vert_pairs.len++;
1281 }
1282 }
1283 }
1284
1285 if (temp_vert_pairs.len == 0) {
1286 use_partial_connect = false;
1287 }
1288 }
1289#endif /* USE_PARTIAL_CONNECT */
1290
1291 uint group_arr_len = 0;
1292 LinkNode *group_head = nullptr;
1293 {
1294 /* scan 'edge_arr' backwards so the outer face boundary is handled first
1295 * (since its likely to be the largest) */
1296 uint edge_index = (edge_arr_len - 1);
1297 uint edge_in_group_tot = 0;
1298
1299 BLI_SMALLSTACK_DECLARE(vstack, BMVert *);
1300
1301 while (true) {
1302 LinkNode *edge_links = nullptr;
1303 uint unique_verts_in_group = 0, unique_edges_in_group = 0;
1304
1305 /* list of groups */
1306 BLI_assert(BM_elem_flag_test(edge_arr[edge_index]->v1, VERT_NOT_IN_STACK));
1307 BLI_SMALLSTACK_PUSH(vstack, edge_arr[edge_index]->v1);
1308 BM_elem_flag_disable(edge_arr[edge_index]->v1, VERT_NOT_IN_STACK);
1309
1310 BMVert *v_iter;
1311 while ((v_iter = static_cast<BMVert *>(BLI_SMALLSTACK_POP(vstack)))) {
1312 unique_verts_in_group++;
1313
1314 BMEdge *e_iter = v_iter->e;
1315 do {
1316 if (BM_elem_flag_test(e_iter, EDGE_NOT_IN_STACK)) {
1318 unique_edges_in_group++;
1319
1320 BLI_linklist_prepend_arena(&edge_links, e_iter, mem_arena);
1321
1322 BMVert *v_other = BM_edge_other_vert(e_iter, v_iter);
1323 if (BM_elem_flag_test(v_other, VERT_NOT_IN_STACK)) {
1324 BLI_SMALLSTACK_PUSH(vstack, v_other);
1326 }
1327 }
1328 } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v_iter)) != v_iter->e);
1329 }
1330
1331 EdgeGroupIsland *g = static_cast<EdgeGroupIsland *>(
1332 BLI_memarena_alloc(mem_arena, sizeof(*g)));
1333 g->vert_len = unique_verts_in_group;
1334 g->edge_len = unique_edges_in_group;
1335 edge_in_group_tot += unique_edges_in_group;
1336
1337 BLI_linklist_prepend_nlink(&group_head, edge_links, (LinkNode *)g);
1338
1339 group_arr_len++;
1340
1341 if (edge_in_group_tot == edge_arr_len) {
1342 break;
1343 }
1344
1345 /* skip edges in the stack */
1346 while (BM_elem_flag_test(edge_arr[edge_index], EDGE_NOT_IN_STACK) == false) {
1347 BLI_assert(edge_index != 0);
1348 edge_index--;
1349 }
1350 }
1351 }
1352
1353 /* Declare here because of `goto` below. */
1354 BMEdge **edge_net_new = nullptr;
1355 BVHTree *bvhtree = nullptr;
1356 float(*vert_coords_backup)[3] = nullptr;
1357 uint *verts_group_table = nullptr;
1358 BMVert **vert_arr = nullptr;
1359 uint vert_arr_len = 0;
1360 EdgeGroupIsland **group_arr = nullptr;
1361
1362 /* single group - no holes */
1363 if (group_arr_len == 1) {
1364 goto finally;
1365 }
1366
1367 /* -------------------------------------------------------------------- */
1368 /* Previous checks need to be kept fast, since they will run very often,
1369 * now we know there are holes, so calculate a spatial lookup info and
1370 * other per-group data.
1371 */
1372
1373 float axis_mat[3][3];
1374 axis_dominant_v3_to_m3(axis_mat, f->no);
1375
1376#define VERT_IN_ARRAY BM_ELEM_INTERNAL_TAG
1377
1378 group_arr = static_cast<EdgeGroupIsland **>(
1379 BLI_memarena_alloc(mem_arena, sizeof(*group_arr) * group_arr_len));
1380 /* sort groups by lowest value vertex */
1381 {
1382 /* fill 'groups_arr' in reverse order so the boundary face is first */
1383 EdgeGroupIsland **group_arr_p = &group_arr[group_arr_len];
1384
1385 for (EdgeGroupIsland *g = static_cast<EdgeGroupIsland *>((void *)group_head); g;
1387 {
1388 LinkNode *edge_links = static_cast<LinkNode *>(g->edge_links.link);
1389
1390 /* init with *any* different verts */
1391 g->vert_span.min = ((BMEdge *)edge_links->link)->v1;
1392 g->vert_span.max = ((BMEdge *)edge_links->link)->v2;
1393 float min_axis[2] = {FLT_MAX, FLT_MAX};
1394 float max_axis[2] = {-FLT_MAX, -FLT_MAX};
1395
1396 do {
1397 BMEdge *e = static_cast<BMEdge *>(edge_links->link);
1399
1400 for (int j = 0; j < 2; j++) {
1401 BMVert *v_iter = (&e->v1)[j];
1402 BLI_assert(v_iter->head.htype == BM_VERT);
1403 /* ideally we could use 'v_iter->co[SORT_AXIS]' here,
1404 * but we need to sort the groups before setting the vertex array order */
1405 const float axis_value[2] = {
1406#if SORT_AXIS == 0
1407 dot_m3_v3_row_x(axis_mat, v_iter->co),
1408 dot_m3_v3_row_y(axis_mat, v_iter->co),
1409#else
1410 dot_m3_v3_row_y(axis_mat, v_iter->co),
1411 dot_m3_v3_row_x(axis_mat, v_iter->co),
1412#endif
1413 };
1414
1415 if (axis_pt_cmp(axis_value, min_axis) == -1) {
1416 g->vert_span.min = v_iter;
1417 copy_v2_v2(min_axis, axis_value);
1418 }
1419 if (axis_pt_cmp(axis_value, max_axis) == 1) {
1420 g->vert_span.max = v_iter;
1421 copy_v2_v2(max_axis, axis_value);
1422 }
1423 }
1424 } while ((edge_links = edge_links->next));
1425
1426 copy_v2_v2(g->vert_span.min_axis, min_axis);
1427 copy_v2_v2(g->vert_span.max_axis, max_axis);
1428
1429 g->has_prev_edge = false;
1430
1431 vert_arr_len += g->vert_len;
1432
1433 *(--group_arr_p) = g;
1434 }
1435 }
1436
1437 qsort(group_arr, group_arr_len, sizeof(*group_arr), group_min_cmp_fn);
1438
1439 /* we don't know how many unique verts there are connecting the edges, so over-alloc */
1440 vert_arr = static_cast<BMVert **>(
1441 BLI_memarena_alloc(mem_arena, sizeof(*vert_arr) * vert_arr_len));
1442 /* map vertex -> group index */
1443 verts_group_table = static_cast<uint *>(
1444 BLI_memarena_alloc(mem_arena, sizeof(*verts_group_table) * vert_arr_len));
1445
1446 vert_coords_backup = static_cast<float(*)[3]>(
1447 BLI_memarena_alloc(mem_arena, sizeof(*vert_coords_backup) * vert_arr_len));
1448
1449 {
1450 /* relative location, for higher precision calculations */
1451 const float f_co_ref[3] = {UNPACK3(BM_FACE_FIRST_LOOP(f)->v->co)};
1452
1453 int v_index = 0; /* global vert index */
1454 for (uint g_index = 0; g_index < group_arr_len; g_index++) {
1455 LinkNode *edge_links = static_cast<LinkNode *>(group_arr[g_index]->edge_links.link);
1456 do {
1457 BMEdge *e = static_cast<BMEdge *>(edge_links->link);
1458 for (int j = 0; j < 2; j++) {
1459 BMVert *v_iter = (&e->v1)[j];
1460 if (!BM_elem_flag_test(v_iter, VERT_IN_ARRAY)) {
1462
1463 /* not nice, but alternatives aren't much better :S */
1464 {
1465 copy_v3_v3(vert_coords_backup[v_index], v_iter->co);
1466
1467 /* for higher precision */
1468 sub_v3_v3(v_iter->co, f_co_ref);
1469
1470 float co_2d[2];
1471 mul_v2_m3v3(co_2d, axis_mat, v_iter->co);
1472 v_iter->co[0] = co_2d[0];
1473 v_iter->co[1] = co_2d[1];
1474 v_iter->co[2] = 0.0f;
1475 }
1476
1477 BM_elem_index_set(v_iter, v_index); /* set_dirty */
1478
1479 vert_arr[v_index] = v_iter;
1480 verts_group_table[v_index] = g_index;
1481 v_index++;
1482 }
1483 }
1484 } while ((edge_links = edge_links->next));
1485 }
1486 }
1487
1489
1490 /* Now create BVH tree.
1491 *
1492 * Note that a large epsilon is used because meshes with dimensions of around 100+ need it.
1493 * see #52329. */
1494 bvhtree = BLI_bvhtree_new(edge_arr_len, 1e-4f, 8, 8);
1495 for (uint i = 0; i < edge_arr_len; i++) {
1496 const float e_cos[2][3] = {
1497 {UNPACK2(edge_arr[i]->v1->co), 0.0f},
1498 {UNPACK2(edge_arr[i]->v2->co), 0.0f},
1499 };
1500 BLI_bvhtree_insert(bvhtree, i, (const float *)e_cos, 2);
1501 }
1502 BLI_bvhtree_balance(bvhtree);
1503
1504#ifdef USE_PARTIAL_CONNECT
1505 if (use_partial_connect) {
1506 /* needs to be done once the vertex indices have been written into */
1507 temp_vert_pairs.remap = static_cast<int *>(
1508 BLI_memarena_alloc(mem_arena, sizeof(*temp_vert_pairs.remap) * vert_arr_len));
1509 copy_vn_i(temp_vert_pairs.remap, vert_arr_len, -1);
1510
1511 TempVertPair *tvp = temp_vert_pairs.list;
1512 do {
1513 temp_vert_pairs.remap[BM_elem_index_get(tvp->v_temp)] = BM_elem_index_get(tvp->v_orig);
1514 } while ((tvp = tvp->next));
1515 }
1516#endif /* USE_PARTIAL_CONNECT */
1517
1518 /* Create connections between groups */
1519
1520 /* may be an over-alloc, but not by much */
1521 edge_net_new_len = uint(edge_net_init_len) + ((group_arr_len - 1) * 2);
1522 edge_net_new = static_cast<BMEdge **>(
1523 BLI_memarena_alloc(mem_arena, sizeof(*edge_net_new) * edge_net_new_len));
1524 memcpy(edge_net_new, edge_net_init, sizeof(*edge_net_new) * size_t(edge_net_init_len));
1525
1526 {
1527 uint edge_net_new_index = edge_net_init_len;
1528 /* start-end of the verts in the current group */
1529
1530 uint vert_range[2];
1531
1532 vert_range[0] = 0;
1533 vert_range[1] = group_arr[0]->vert_len;
1534
1536 args.bvhtree = bvhtree;
1537
1538 /* use the new edge array so we can scan edges which have been added */
1539 args.edge_arr = edge_arr;
1540 args.edge_arr_len = edge_arr_len;
1541
1542 /* we only want to check newly created edges */
1543 args.edge_arr_new = edge_net_new + edge_net_init_len;
1544 args.edge_arr_new_len = 0;
1545
1546 args.vert_range = vert_range;
1547
1548 for (uint g_index = 1; g_index < group_arr_len; g_index++) {
1549 EdgeGroupIsland *g = group_arr[g_index];
1550
1551 /* The range of verts this group uses in 'verts_arr' (not including the last index). */
1552 vert_range[0] = vert_range[1];
1553 vert_range[1] += g->vert_len;
1554
1555 if (g->has_prev_edge == false) {
1556 BMVert *v_origin = g->vert_span.min;
1557 /* Index of BMVert for the edge group connection with `v_origin`. */
1558 const int index_other = bm_face_split_edgenet_find_connection(&args, v_origin, false);
1559 // BLI_assert(index_other >= 0 && index_other < int(vert_arr_len));
1560
1561 /* only for degenerate geometry */
1562 if (index_other != -1) {
1563#ifdef USE_PARTIAL_CONNECT
1564 if ((use_partial_connect == false) ||
1566 temp_vert_pairs.remap, BM_elem_index_get(v_origin), index_other) == false))
1567#endif
1568 {
1569 BMVert *v_end = vert_arr[index_other];
1570
1571 edge_net_new[edge_net_new_index] = BM_edge_create(
1572 bm, v_origin, v_end, nullptr, eBMCreateFlag(0));
1573#ifdef USE_PARTIAL_CONNECT
1574 BM_elem_index_set(edge_net_new[edge_net_new_index], edge_net_new_index);
1575#endif
1576 edge_net_new_index++;
1577 args.edge_arr_new_len++;
1578 }
1579 }
1580 }
1581
1582 {
1583 BMVert *v_origin = g->vert_span.max;
1584 /* Index of BMVert for the edge group connection with `v_origin`. */
1585 const int index_other = bm_face_split_edgenet_find_connection(&args, v_origin, true);
1586 // BLI_assert(index_other >= 0 && index_other < int(vert_arr_len));
1587
1588 /* only for degenerate geometry */
1589 if (index_other != -1) {
1590#ifdef USE_PARTIAL_CONNECT
1591 if ((use_partial_connect == false) ||
1593 temp_vert_pairs.remap, BM_elem_index_get(v_origin), index_other) == false))
1594#endif
1595 {
1596 BMVert *v_end = vert_arr[index_other];
1597 edge_net_new[edge_net_new_index] = BM_edge_create(
1598 bm, v_origin, v_end, nullptr, eBMCreateFlag(0));
1599#ifdef USE_PARTIAL_CONNECT
1600 BM_elem_index_set(edge_net_new[edge_net_new_index], edge_net_new_index);
1601#endif
1602 edge_net_new_index++;
1603 args.edge_arr_new_len++;
1604 }
1605
1606 /* tell the 'next' group it doesn't need to create its own back-link */
1607 uint g_index_other = verts_group_table[index_other];
1608 group_arr[g_index_other]->has_prev_edge = true;
1609 }
1610 }
1611 }
1612 BLI_assert(edge_net_new_len >= edge_net_new_index);
1613 edge_net_new_len = edge_net_new_index;
1614 }
1615
1616 BLI_bvhtree_free(bvhtree);
1617
1618 *r_edge_net_new = edge_net_new;
1619 *r_edge_net_new_len = edge_net_new_len;
1620 ok = true;
1621
1622 for (uint i = 0; i < vert_arr_len; i++) {
1623 copy_v3_v3(vert_arr[i]->co, vert_coords_backup[i]);
1624 }
1625
1626finally:
1627
1628#ifdef USE_PARTIAL_CONNECT
1629 /* don't free 'vert_temp_pair_list', its part of the arena */
1630 if (use_partial_connect) {
1631
1632/* Sanity check: ensure we don't have connecting edges before splicing begins. */
1633# ifndef NDEBUG
1634 {
1635 TempVertPair *tvp = temp_vert_pairs.list;
1636 do {
1637 /* We must _never_ create connections here
1638 * (in case the islands can't have a connection at all). */
1639 BLI_assert(BM_edge_exists(tvp->v_orig, tvp->v_temp) == nullptr);
1640 } while ((tvp = tvp->next));
1641 }
1642# endif
1643
1644 TempVertPair *tvp = temp_vert_pairs.list;
1645 do {
1646 /* its _very_ unlikely the edge exists,
1647 * however splicing may cause this. see: #48012 */
1648 if (!BM_edge_exists(tvp->v_orig, tvp->v_temp)) {
1649 BM_vert_splice(bm, tvp->v_orig, tvp->v_temp);
1650 }
1651 } while ((tvp = tvp->next));
1652
1653 /* Remove edges which have become doubles since splicing vertices together,
1654 * its less trouble than detecting future-doubles on edge-creation. */
1655 for (uint i = edge_net_init_len; i < edge_net_new_len; i++) {
1656 while (BM_edge_find_double(edge_net_new[i])) {
1657 BM_edge_kill(bm, edge_net_new[i]);
1658 edge_net_new_len--;
1659 if (i == edge_net_new_len) {
1660 break;
1661 }
1662 edge_net_new[i] = edge_net_new[edge_net_new_len];
1663 }
1664 }
1665
1666 *r_edge_net_new_len = edge_net_new_len;
1667 }
1668#endif
1669
1670 for (uint i = 0; i < edge_arr_len; i++) {
1672 BM_elem_flag_disable(edge_arr[i]->v1, VERT_NOT_IN_STACK);
1674 }
1675
1676#undef VERT_IN_ARRAY
1677#undef VERT_NOT_IN_STACK
1678#undef EDGE_NOT_IN_STACK
1679
1680 return ok;
1681}
1682
1683#undef SORT_AXIS
1684
CustomData interface, see also DNA_customdata_types.h.
void CustomData_bmesh_interp(CustomData *data, const void **src_blocks, const float *weights, const float *sub_weights, int count, void *dst_block)
void CustomData_bmesh_copy_block(CustomData &data, void *src_block, void **dst_block)
bool CustomData_has_math(const CustomData *data)
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:25
#define BLI_assert(a)
Definition BLI_assert.h:50
#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
Definition BLI_kdopbvh.h:92
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(struct 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.c: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)
@ BM_ELEM_SELECT
@ BM_ELEM_INTERNAL_TAG
#define BM_FACE_FIRST_LOOP(p)
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:43
void BM_vert_separate_tested_edges(BMesh *, BMVert *v_dst, BMVert *v_src, bool(*testfn)(BMEdge *, void *arg), void *arg)
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:23
@ BM_CREATE_NOP
Definition bmesh_core.hh:24
#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
ATTR_WARN_UNUSED_RESULT BMesh * bm
#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 VERT_VISIT
#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()
#define printf
int len
draw_view in_light_buf[] float
int count
static MemArena * mem_arena
Definition makesdna.cc:62
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
static ulong * next
#define swap(a, b)
Definition sort.c:55
#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 BMLoop * radial_next
struct BMFace * f
struct BMLoop * next
float co[3]
struct BMEdge * e
BMHeader head
int totfacesel
char elem_index_dirty
CustomData ldata
struct EdgeGroupIsland::@156 vert_span
void * link
struct LinkNode * next