Blender V4.3
bmo_bridge.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#include "BLI_listbase.h"
12#include "BLI_math_vector.h"
13#include "BLI_utildefines.h"
14
15#include "bmesh.hh"
16
17#include "intern/bmesh_operators_private.hh" /* own include */
18
24#define USE_DUPLICATE_FACE_VERT_CHECK
25
26#define EDGE_MARK 4
27#define EDGE_OUT 8
28#define FACE_OUT 16
29
30/* el_a and el_b _must_ be same size */
32 LinkData *el_a,
33 LinkData *el_b,
34 const float merge_factor)
35{
36 BMOperator op_weld;
37 BMOpSlot *slot_targetmap;
38
39 BMO_op_init(bm, &op_weld, 0, "weld_verts");
40
41 slot_targetmap = BMO_slot_get(op_weld.slots_in, "targetmap");
42
43 do {
44 BMVert *v_a = static_cast<BMVert *>(el_a->data), *v_b = static_cast<BMVert *>(el_b->data);
45 BM_data_interp_from_verts(bm, v_a, v_b, v_b, merge_factor);
46 interp_v3_v3v3(v_b->co, v_a->co, v_b->co, merge_factor);
47 BLI_assert(v_a != v_b);
48 BMO_slot_map_elem_insert(&op_weld, slot_targetmap, v_a, v_b);
49 } while ((void)(el_b = el_b->next), (el_a = el_a->next));
50
51 BMO_op_exec(bm, &op_weld);
52 BMO_op_finish(bm, &op_weld);
53}
54
55/* get the 2 loops matching 2 verts.
56 * first attempt to get the face corners that use the edge defined by v1 & v2,
57 * if that fails just get any loop that's on the vert (the first one) */
58static void bm_vert_loop_pair(BMesh *bm, BMVert *v1, BMVert *v2, BMLoop **l1, BMLoop **l2)
59{
60 BMEdge *e = BM_edge_exists(v1, v2);
61 BMLoop *l = e->l;
62
63 if (l) {
64 if (l->v == v1) {
65 *l1 = l;
66 *l2 = l->next;
67 }
68 else {
69 *l2 = l;
70 *l1 = l->next;
71 }
72 }
73 else {
74 /* fallback to _any_ loop */
75 *l1 = static_cast<BMLoop *>(BM_iter_at_index(bm, BM_LOOPS_OF_VERT, v1, 0));
76 *l2 = static_cast<BMLoop *>(BM_iter_at_index(bm, BM_LOOPS_OF_VERT, v2, 0));
77 }
78}
79
80/* el_b can have any offset */
82 LinkData *el_b,
83 LinkData *el_b_first,
84 const float len_max)
85{
86 float len = 0.0f;
87 BLI_assert(el_a->prev == nullptr); /* must be first */
88 do {
89 len += len_v3v3(((BMVert *)el_a->data)->co, ((BMVert *)el_b->data)->co);
90 } while ((void)(el_b = el_b->next ? el_b->next : el_b_first),
91 (el_a = el_a->next) && (len < len_max));
92 return len;
93}
94
95static void bm_bridge_best_rotation(BMEdgeLoopStore *el_store_a, BMEdgeLoopStore *el_store_b)
96{
97 ListBase *lb_a = BM_edgeloop_verts_get(el_store_a);
98 ListBase *lb_b = BM_edgeloop_verts_get(el_store_b);
99 LinkData *el_a = static_cast<LinkData *>(lb_a->first);
100 LinkData *el_b = static_cast<LinkData *>(lb_b->first);
101 LinkData *el_b_first = el_b;
102 LinkData *el_b_best = nullptr;
103
104 float len_best = FLT_MAX;
105
106 for (; el_b; el_b = el_b->next) {
107 const float len = bm_edgeloop_offset_length(el_a, el_b, el_b_first, len_best);
108 if (len < len_best) {
109 el_b_best = el_b;
110 len_best = len;
111 }
112 }
113
114 if (el_b_best) {
115 BLI_listbase_rotate_first(lb_b, el_b_best);
116 }
117}
118
120{
121 BMLoop *l_iter, *l_first;
122 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
123 do {
125 } while ((l_iter = l_iter->next) != l_first);
126}
127
128static bool bm_edge_test_cb(BMEdge *e, void *bm_v)
129{
130 return BMO_edge_flag_test((BMesh *)bm_v, e, EDGE_MARK);
131}
132
134 BMEdgeLoopStore *el_store_a,
135 BMEdgeLoopStore *el_store_b,
136 const bool use_merge,
137 const float merge_factor,
138 const int twist_offset)
139{
140 const float eps = 0.00001f;
141 LinkData *el_a_first, *el_b_first;
142 const bool is_closed = BM_edgeloop_is_closed(el_store_a) && BM_edgeloop_is_closed(el_store_b);
143 int el_store_a_len, el_store_b_len;
144 bool el_store_b_free = false;
145 float el_dir[3];
146 float dot_a, dot_b;
147 const bool use_edgeout = true;
148
149 el_store_a_len = BM_edgeloop_length_get((BMEdgeLoopStore *)el_store_a);
150 el_store_b_len = BM_edgeloop_length_get((BMEdgeLoopStore *)el_store_b);
151
152 if (el_store_a_len < el_store_b_len) {
153 std::swap(el_store_a_len, el_store_b_len);
154 std::swap(el_store_a, el_store_b);
155 }
156
157 if (use_merge) {
158 BLI_assert(el_store_a_len == el_store_b_len);
159 }
160
161 if (el_store_a_len != el_store_b_len) {
163 }
164
165 sub_v3_v3v3(el_dir, BM_edgeloop_center_get(el_store_a), BM_edgeloop_center_get(el_store_b));
166
167 if (is_closed) {
168 /* if all loops are closed this will calculate twice for all loops */
169 BM_edgeloop_calc_normal(bm, el_store_a);
170 BM_edgeloop_calc_normal(bm, el_store_b);
171 }
172 else {
173 ListBase *lb_a = BM_edgeloop_verts_get(el_store_a);
174 ListBase *lb_b = BM_edgeloop_verts_get(el_store_b);
175
176 /* normalizing isn't strictly needed but without we may get very large values */
177 float no[3];
178 float dir_a_orig[3], dir_b_orig[3];
179 float dir_a[3], dir_b[3];
180 const float *test_a, *test_b;
181
182 sub_v3_v3v3(dir_a_orig,
183 ((BMVert *)(((LinkData *)lb_a->first)->data))->co,
184 ((BMVert *)(((LinkData *)lb_a->last)->data))->co);
185 sub_v3_v3v3(dir_b_orig,
186 ((BMVert *)(((LinkData *)lb_b->first)->data))->co,
187 ((BMVert *)(((LinkData *)lb_b->last)->data))->co);
188
189 /* make the directions point out from the normals, 'no' is used as a temp var */
190 cross_v3_v3v3(no, dir_a_orig, el_dir);
191 cross_v3_v3v3(dir_a, no, el_dir);
192 cross_v3_v3v3(no, dir_b_orig, el_dir);
193 cross_v3_v3v3(dir_b, no, el_dir);
194
195 if (LIKELY(!is_zero_v3(dir_a) && !is_zero_v3(dir_b))) {
196 test_a = dir_a;
197 test_b = dir_b;
198 }
199 else {
214 test_a = dir_a_orig;
215 test_b = dir_b_orig;
216 }
217
218 if (dot_v3v3(test_a, test_b) < 0.0f) {
219 BM_edgeloop_flip(bm, el_store_b);
220 }
221
222 normalize_v3_v3(no, el_dir);
223 BM_edgeloop_calc_normal_aligned(bm, el_store_a, no);
224 BM_edgeloop_calc_normal_aligned(bm, el_store_b, no);
225 }
226
227 dot_a = dot_v3v3(BM_edgeloop_normal_get(el_store_a), el_dir);
228 dot_b = dot_v3v3(BM_edgeloop_normal_get(el_store_b), el_dir);
229
230 if (UNLIKELY((len_squared_v3(el_dir) < eps) || ((fabsf(dot_a) < eps) && (fabsf(dot_b) < eps)))) {
231 /* in this case there is no depth between the two loops,
232 * eg: 2x 2d circles, one scaled smaller,
233 * in this case 'el_dir' can't be used, just ensure we have matching flipping. */
234 if (dot_v3v3(BM_edgeloop_normal_get(el_store_a), BM_edgeloop_normal_get(el_store_b)) < 0.0f) {
235 BM_edgeloop_flip(bm, el_store_b);
236 }
237 }
238 else if ((dot_a < 0.0f) != (dot_b < 0.0f)) {
239 BM_edgeloop_flip(bm, el_store_b);
240 }
241
242 /* we only care about flipping if we make faces */
243 if (use_merge == false) {
244 float no[3];
245
246 add_v3_v3v3(no, BM_edgeloop_normal_get(el_store_a), BM_edgeloop_normal_get(el_store_b));
247
248 if (dot_v3v3(no, el_dir) < 0.0f) {
249 BM_edgeloop_flip(bm, el_store_a);
250 BM_edgeloop_flip(bm, el_store_b);
251 }
252
253 /* vote on winding (so new face winding is based on existing connected faces) */
254 if (bm->totface) {
255 BMEdgeLoopStore *estore_pair[2] = {el_store_a, el_store_b};
256 int i;
257 int winding_votes[2] = {0, 0};
258 int winding_dir = 1;
259 for (i = 0; i < 2; i++, winding_dir = -winding_dir) {
260 LISTBASE_FOREACH (LinkData *, el, BM_edgeloop_verts_get(estore_pair[i])) {
261 LinkData *el_next = BM_EDGELINK_NEXT(estore_pair[i], el);
262 if (el_next) {
263 BMEdge *e = BM_edge_exists(static_cast<BMVert *>(el->data),
264 static_cast<BMVert *>(el_next->data));
265 if (e && BM_edge_is_boundary(e)) {
266 winding_votes[i] += ((e->l->v == el->data) ? winding_dir : -winding_dir);
267 }
268 }
269 }
270 }
271
272 if (winding_votes[0] || winding_votes[1]) {
273 bool flip[2] = {false, false};
274
275 /* for direction aligned loops we can't rely on the directly we have,
276 * use the winding defined by the connected faces (see #48356). */
277 if (fabsf(dot_a) < eps) {
278 if (winding_votes[0] < 0) {
279 flip[0] = !flip[0];
280 winding_votes[0] *= -1;
281 }
282 }
283 if (fabsf(dot_b) < eps) {
284 if (winding_votes[1] < 0) {
285 flip[1] = !flip[1];
286 winding_votes[1] *= -1;
287 }
288 }
289
290 /* when both loops contradict the winding, flip them so surrounding geometry matches */
291 if ((winding_votes[0] + winding_votes[1]) < 0) {
292 flip[0] = !flip[0];
293 flip[1] = !flip[1];
294
295/* valid but unused */
296#if 0
297 winding_votes[0] *= -1;
298 winding_votes[1] *= -1;
299#endif
300 }
301
302 if (flip[0]) {
303 BM_edgeloop_flip(bm, el_store_a);
304 }
305 if (flip[1]) {
306 BM_edgeloop_flip(bm, el_store_b);
307 }
308 }
309 }
310 }
311
312 if (el_store_a_len > el_store_b_len) {
313 el_store_b = BM_edgeloop_copy(el_store_b);
314 BM_edgeloop_expand(bm, el_store_b, el_store_a_len, false, nullptr);
315 el_store_b_free = true;
316 }
317
318 if (is_closed) {
319 bm_bridge_best_rotation(el_store_a, el_store_b);
320
321 /* add twist */
322 if (twist_offset != 0) {
323 const int len_b = BM_edgeloop_length_get(el_store_b);
324 ListBase *lb_b = BM_edgeloop_verts_get(el_store_b);
325 LinkData *el_b = static_cast<LinkData *>(BLI_rfindlink(lb_b, mod_i(twist_offset, len_b)));
326 BLI_listbase_rotate_first(lb_b, el_b);
327 }
328 }
329
330 /* Assign after flipping is finalized */
331 el_a_first = static_cast<LinkData *>(BM_edgeloop_verts_get(el_store_a)->first);
332 el_b_first = static_cast<LinkData *>(BM_edgeloop_verts_get(el_store_b)->first);
333
334 if (use_merge) {
335 bm_bridge_splice_loops(bm, el_a_first, el_b_first, merge_factor);
336 }
337 else {
338 LinkData *el_a = el_a_first;
339 LinkData *el_b = el_b_first;
340
341 LinkData *el_a_next;
342 LinkData *el_b_next;
343
344 while (true) {
345 BMFace *f, *f_example;
346 BMLoop *l_iter;
347 BMVert *v_a, *v_b, *v_a_next, *v_b_next;
348
349 BMLoop *l_a = nullptr;
350 BMLoop *l_b = nullptr;
351 BMLoop *l_a_next = nullptr;
352 BMLoop *l_b_next = nullptr;
353
354 if (is_closed) {
355 el_a_next = BM_EDGELINK_NEXT(el_store_a, el_a);
356 el_b_next = BM_EDGELINK_NEXT(el_store_b, el_b);
357 }
358 else {
359 el_a_next = el_a->next;
360 el_b_next = el_b->next;
361 if (ELEM(nullptr, el_a_next, el_b_next)) {
362 break;
363 }
364 }
365
366 v_a = static_cast<BMVert *>(el_a->data);
367 v_b = static_cast<BMVert *>(el_b->data);
368 v_a_next = static_cast<BMVert *>(el_a_next->data);
369 v_b_next = static_cast<BMVert *>(el_b_next->data);
370
371 /* get loop data - before making the face */
372 if (v_b != v_b_next) {
373 bm_vert_loop_pair(bm, v_a, v_a_next, &l_a, &l_a_next);
374 bm_vert_loop_pair(bm, v_b, v_b_next, &l_b, &l_b_next);
375 }
376 else {
377 /* lazy, could be more clever here */
378 bm_vert_loop_pair(bm, v_a, v_a_next, &l_a, &l_a_next);
379 l_b = l_b_next = static_cast<BMLoop *>(BM_iter_at_index(bm, BM_LOOPS_OF_VERT, v_b, 0));
380 }
381
382 if (l_a && l_a_next == nullptr) {
383 l_a_next = l_a;
384 }
385 if (l_a_next && l_a == nullptr) {
386 l_a = l_a_next;
387 }
388 if (l_b && l_b_next == nullptr) {
389 l_b_next = l_b;
390 }
391 if (l_b_next && l_b == nullptr) {
392 l_b = l_b_next;
393 }
394 f_example = l_a ? l_a->f : (l_b ? l_b->f : nullptr);
395
396 if (v_b != v_b_next) {
397#ifdef USE_DUPLICATE_FACE_VERT_CHECK /* Only check for duplicates between loops. */
398 BLI_assert((v_b != v_b_next) && (v_a_next != v_a));
399 if (UNLIKELY(ELEM(v_b, v_a_next, v_a) || ELEM(v_b_next, v_a_next, v_a))) {
400 f = nullptr;
401 }
402 else
403#endif
404 {
405 BMVert *v_arr[4] = {v_b, v_b_next, v_a_next, v_a};
406 f = BM_face_exists(v_arr, 4);
407 if (f == nullptr) {
408 /* copy if loop data if its is missing on one ring */
409 f = BM_face_create_verts(bm, v_arr, 4, nullptr, BM_CREATE_NOP, true);
410
411 l_iter = BM_FACE_FIRST_LOOP(f);
412 if (l_b) {
413 BM_elem_attrs_copy(bm, l_b, l_iter);
414 }
415 l_iter = l_iter->next;
416 if (l_b_next) {
417 BM_elem_attrs_copy(bm, l_b_next, l_iter);
418 }
419 l_iter = l_iter->next;
420 if (l_a_next) {
421 BM_elem_attrs_copy(bm, l_a_next, l_iter);
422 }
423 l_iter = l_iter->next;
424 if (l_a) {
425 BM_elem_attrs_copy(bm, l_a, l_iter);
426 }
427 }
428 }
429 }
430 else {
431#ifdef USE_DUPLICATE_FACE_VERT_CHECK /* Only check for duplicates between loops. */
432 BLI_assert(v_a_next != v_a);
433 if (UNLIKELY(ELEM(v_b, v_a_next, v_a))) {
434 f = nullptr;
435 }
436 else
437#endif
438 {
439 BMVert *v_arr[3] = {v_b, v_a_next, v_a};
440 f = BM_face_exists(v_arr, 3);
441 if (f == nullptr) {
442 /* fan-fill a triangle */
443 f = BM_face_create_verts(bm, v_arr, 3, nullptr, BM_CREATE_NOP, true);
444
445 l_iter = BM_FACE_FIRST_LOOP(f);
446 if (l_b) {
447 BM_elem_attrs_copy(bm, l_b, l_iter);
448 }
449 l_iter = l_iter->next;
450 if (l_a_next) {
451 BM_elem_attrs_copy(bm, l_a_next, l_iter);
452 }
453 l_iter = l_iter->next;
454 if (l_a) {
455 BM_elem_attrs_copy(bm, l_a, l_iter);
456 }
457 }
458 }
459 }
460
461#ifdef USE_DUPLICATE_FACE_VERT_CHECK
462 if (f != nullptr)
463#endif
464 {
465 if (f_example && (f_example != f)) {
466 BM_elem_attrs_copy(bm, f_example, f);
467 }
470
471 /* tag all edges of the face, untag the loop edges after */
472 if (use_edgeout) {
474 }
475 }
476
477 if (el_a_next == el_a_first) {
478 break;
479 }
480
481 el_a = el_a_next;
482 el_b = el_b_next;
483 }
484 }
485
486 if (el_store_a_len != el_store_b_len) {
487 BMEdgeLoopStore *estore_pair[2] = {el_store_a, el_store_b};
488 int i;
489
491 /* when we have to bridge between different sized edge-loops,
492 * be clever and post-process for best results */
493
494 /* triangulate inline */
495 BMO_op_initf(bm, &op_sub, 0, "triangulate faces=%hf", BM_ELEM_TAG, true);
496 /* calc normals for input faces before executing */
497 {
498 BMOIter siter;
499 BMFace *f;
500 BMO_ITER (f, &siter, op_sub.slots_in, "faces", BM_FACE) {
502 }
503 }
505 BMO_slot_buffer_flag_enable(bm, op_sub.slots_out, "faces.out", BM_FACE, FACE_OUT);
506 BMO_slot_buffer_hflag_enable(bm, op_sub.slots_out, "faces.out", BM_FACE, BM_ELEM_TAG, false);
508
509 /* tag verts on each side so we can restrict rotation of edges to verts on the same side */
510 for (i = 0; i < 2; i++) {
511 LISTBASE_FOREACH (LinkData *, el, BM_edgeloop_verts_get(estore_pair[i])) {
512 BM_elem_flag_set((BMVert *)el->data, BM_ELEM_TAG, i);
513 }
514 }
515
517 &op_sub,
518 0,
519 "beautify_fill faces=%hf edges=ae use_restrict_tag=%b method=%i",
521 true,
522 1);
523
524 if (use_edgeout) {
525 BMOIter siter;
526 BMFace *f;
527 BMO_ITER (f, &siter, op_sub.slots_in, "faces", BM_FACE) {
530 }
531 }
532
534 /* there may also be tagged faces that didn't rotate, mark input */
535
536 if (use_edgeout) {
537 BMOIter siter;
538 BMFace *f;
539 BMO_ITER (f, &siter, op_sub.slots_out, "geom.out", BM_FACE) {
542 }
543 }
544 else {
545 BMO_slot_buffer_flag_enable(bm, op_sub.slots_out, "geom.out", BM_FACE, FACE_OUT);
546 }
547
549 }
550
551 if (use_edgeout && use_merge == false) {
552 /* we've enabled all face edges above, now disable all loop edges */
553 BMEdgeLoopStore *estore_pair[2] = {el_store_a, el_store_b};
554 int i;
555 for (i = 0; i < 2; i++) {
556 LISTBASE_FOREACH (LinkData *, el, BM_edgeloop_verts_get(estore_pair[i])) {
557 LinkData *el_next = BM_EDGELINK_NEXT(estore_pair[i], el);
558 if (el_next) {
559 if (el->data != el_next->data) {
560 BMEdge *e = BM_edge_exists(static_cast<BMVert *>(el->data),
561 static_cast<BMVert *>(el_next->data));
563 }
564 }
565 }
566 }
567 }
568
569 if (el_store_b_free) {
570 BM_edgeloop_free(el_store_b);
571 }
572}
573
575{
576 ListBase eloops = {nullptr};
577
578 /* merge-bridge support */
579 const bool use_pairs = BMO_slot_bool_get(op->slots_in, "use_pairs");
580 const bool use_merge = BMO_slot_bool_get(op->slots_in, "use_merge");
581 const float merge_factor = BMO_slot_float_get(op->slots_in, "merge_factor");
582 const bool use_cyclic = BMO_slot_bool_get(op->slots_in, "use_cyclic") && (use_merge == false);
583 const int twist_offset = BMO_slot_int_get(op->slots_in, "twist_offset");
584 int count;
585 bool changed = false;
586
588
590
592
593 if (count < 2) {
594 BMO_error_raise(bm, op, BMO_ERROR_CANCEL, "Select at least two edge loops");
595 goto cleanup;
596 }
597
598 if (use_pairs && (count % 2)) {
599 BMO_error_raise(bm, op, BMO_ERROR_CANCEL, "Select an even number of loops to bridge pairs");
600 goto cleanup;
601 }
602
603 if (use_merge) {
604 bool match = true;
605 const int eloop_len = BM_edgeloop_length_get(static_cast<BMEdgeLoopStore *>(eloops.first));
606 LISTBASE_FOREACH (LinkData *, el_store, &eloops) {
607 if (eloop_len != BM_edgeloop_length_get((BMEdgeLoopStore *)el_store)) {
608 match = false;
609 break;
610 }
611 }
612 if (!match) {
613 BMO_error_raise(bm, op, BMO_ERROR_CANCEL, "Selected loops must have equal edge counts");
614 goto cleanup;
615 }
616 }
617
618 if (count > 2) {
619 if (use_pairs) {
621 }
622 BM_mesh_edgeloops_calc_order(bm, &eloops, use_pairs);
623 }
624
625 LISTBASE_FOREACH (LinkData *, el_store, &eloops) {
626 LinkData *el_store_next = el_store->next;
627
628 if (el_store_next == nullptr) {
629 if (use_cyclic && (count > 2)) {
630 el_store_next = static_cast<LinkData *>(eloops.first);
631 }
632 else {
633 break;
634 }
635 }
636
638 (BMEdgeLoopStore *)el_store,
639 (BMEdgeLoopStore *)el_store_next,
640 use_merge,
641 merge_factor,
642 twist_offset);
643 if (use_pairs) {
644 el_store = el_store->next;
645 }
646 changed = true;
647 }
648
649cleanup:
650 BM_mesh_edgeloops_free(&eloops);
651
652 if (changed) {
653 if (use_merge == false) {
656 }
657 }
658}
#define BLI_assert(a)
Definition BLI_assert.h:50
void * BLI_rfindlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define LISTBASE_FOREACH(type, var, list)
void BLI_listbase_rotate_first(struct ListBase *lb, void *vlink) ATTR_NONNULL(1
MINLINE int mod_i(int i, int n)
MINLINE float len_squared_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
Definition math_vector.c:36
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float normalize_v3_v3(float r[3], const float a[3])
MINLINE bool is_zero_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
#define UNLIKELY(x)
#define ELEM(...)
#define LIKELY(x)
@ BM_ELEM_TAG
#define BM_FACE_FIRST_LOOP(p)
void BM_elem_attrs_copy(BMesh *bm, const BMCustomDataCopyMap &map, const BMVert *src, BMVert *dst)
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)
@ BM_CREATE_NOP
Definition bmesh_core.hh:24
void BM_mesh_edgeloops_calc_order(BMesh *, ListBase *eloops, const bool use_normals)
void BM_edgeloop_expand(BMesh *bm, BMEdgeLoopStore *el_store, int el_store_len, bool split, GSet *split_edges)
void BM_mesh_edgeloops_free(ListBase *eloops)
void BM_edgeloop_free(BMEdgeLoopStore *el_store)
void BM_edgeloop_flip(BMesh *, BMEdgeLoopStore *el_store)
BMEdgeLoopStore * BM_edgeloop_copy(BMEdgeLoopStore *el_store)
bool BM_edgeloop_calc_normal(BMesh *, BMEdgeLoopStore *el_store)
int BM_mesh_edgeloops_find(BMesh *bm, ListBase *r_eloops, bool(*test_fn)(BMEdge *, void *user_data), void *user_data)
int BM_edgeloop_length_get(BMEdgeLoopStore *el_store)
const float * BM_edgeloop_normal_get(BMEdgeLoopStore *el_store)
bool BM_edgeloop_calc_normal_aligned(BMesh *, BMEdgeLoopStore *el_store, const float no_align[3])
bool BM_edgeloop_is_closed(BMEdgeLoopStore *el_store)
const float * BM_edgeloop_center_get(BMEdgeLoopStore *el_store)
void BM_mesh_edgeloops_calc_normal(BMesh *bm, ListBase *eloops)
ListBase * BM_edgeloop_verts_get(BMEdgeLoopStore *el_store)
void BM_mesh_edgeloops_calc_center(BMesh *bm, ListBase *eloops)
#define BM_EDGELINK_NEXT(el_store, elink)
@ BMO_ERROR_CANCEL
void BMO_error_raise(BMesh *bm, BMOperator *owner, eBMOpErrorLevel level, const char *msg) ATTR_NONNULL(1
#define BM_elem_flag_set(ele, hflag, val)
#define BM_elem_flag_enable(ele, hflag)
void BM_data_interp_from_verts(BMesh *bm, const BMVert *v_src_1, const BMVert *v_src_2, BMVert *v_dst, const float fac)
Data, Interpolate From Verts.
void * BM_iter_at_index(BMesh *bm, const char itype, void *data, int index)
@ BM_LOOPS_OF_VERT
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_mesh_elem_hflag_disable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
#define BM_FACE
#define BM_EDGE
void BMO_slot_buffer_flag_enable(BMesh *bm, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, short oflag)
BMO_FLAG_BUFFER.
void BMO_slot_buffer_hflag_enable(BMesh *bm, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, char hflag, bool do_flush)
BMO_FLAG_BUFFER.
#define BMO_edge_flag_test(bm, e, oflag)
#define BMO_edge_flag_enable(bm, e, oflag)
float BMO_slot_float_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
BMOpSlot * BMO_slot_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *identifier)
BMESH OPSTACK GET SLOT.
void BMO_op_exec(BMesh *bm, BMOperator *op)
BMESH OPSTACK EXEC OP.
void BMO_slot_buffer_from_enabled_flag(BMesh *bm, BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, short oflag)
#define BMO_face_flag_enable(bm, e, oflag)
void BMO_op_init(BMesh *bm, BMOperator *op, int flag, const char *opname)
BMESH OPSTACK INIT OP.
#define BMO_ITER(ele, iter, slot_args, slot_name, restrict_flag)
bool BMO_op_initf(BMesh *bm, BMOperator *op, int flag, const char *fmt,...)
int BMO_slot_int_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
void BMO_op_finish(BMesh *bm, BMOperator *op)
BMESH OPSTACK FINISH OP.
#define BMO_edge_flag_disable(bm, e, oflag)
bool BMO_slot_bool_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
BLI_INLINE void BMO_slot_map_elem_insert(BMOperator *op, BMOpSlot *slot, const void *element, void *val)
void BM_face_normal_update(BMFace *f)
BMFace * BM_face_exists(BMVert *const *varr, int len)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
BLI_INLINE bool BM_edge_is_boundary(const BMEdge *e) 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 BMLoop * l_b
static void bm_bridge_splice_loops(BMesh *bm, LinkData *el_a, LinkData *el_b, const float merge_factor)
Definition bmo_bridge.cc:31
void bmo_bridge_loops_exec(BMesh *bm, BMOperator *op)
#define FACE_OUT
Definition bmo_bridge.cc:28
#define EDGE_OUT
Definition bmo_bridge.cc:27
static void bm_vert_loop_pair(BMesh *bm, BMVert *v1, BMVert *v2, BMLoop **l1, BMLoop **l2)
Definition bmo_bridge.cc:58
static void bm_bridge_best_rotation(BMEdgeLoopStore *el_store_a, BMEdgeLoopStore *el_store_b)
Definition bmo_bridge.cc:95
#define EDGE_MARK
Definition bmo_bridge.cc:26
static void bridge_loop_pair(BMesh *bm, BMEdgeLoopStore *el_store_a, BMEdgeLoopStore *el_store_b, const bool use_merge, const float merge_factor, const int twist_offset)
static float bm_edgeloop_offset_length(LinkData *el_a, LinkData *el_b, LinkData *el_b_first, const float len_max)
Definition bmo_bridge.cc:81
static bool bm_edge_test_cb(BMEdge *e, void *bm_v)
static void bm_face_edges_tag_out(BMesh *bm, BMFace *f)
#define fabsf(x)
int len
static double op_sub(double a, double b)
int count
const btScalar eps
Definition poly34.cpp:11
#define FLT_MAX
Definition stdcycles.h:14
BMEdgeLoopStore * next
struct BMVert * v
struct BMEdge * e
struct BMFace * f
struct BMLoop * next
struct BMOpSlot slots_out[BMO_OP_MAX_SLOTS]
struct BMOpSlot slots_in[BMO_OP_MAX_SLOTS]
float co[3]
int totface
void * data
struct LinkData * next
struct LinkData * prev
void * last
void * first