Blender V4.3
bmo_connect.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_alloca.h"
12#include "BLI_linklist_stack.h"
13#include "BLI_utildefines.h"
15
16#include "bmesh.hh"
17
18#include "intern/bmesh_operators_private.hh" /* own include */
19
20#define VERT_INPUT 1
21
22#define EDGE_OUT 1
23/* Edge spans 2 VERT_INPUT's, its a NOP,
24 * but include in "edges.out" */
25#define EDGE_OUT_ADJ 2
26
27#define FACE_TAG 2
28#define FACE_EXCLUDE 4
29
30static int bm_face_connect_verts(BMesh *bm, BMFace *f, const bool check_degenerate)
31{
32 const uint pair_split_max = f->len / 2;
33 BMLoop *(*loops_split)[2] = BLI_array_alloca(loops_split, pair_split_max);
34 STACK_DECLARE(loops_split);
35 BMVert *(*verts_pair)[2] = BLI_array_alloca(verts_pair, pair_split_max);
36 STACK_DECLARE(verts_pair);
37
38 BMLoop *l_tag_prev = nullptr, *l_tag_first = nullptr;
39 BMLoop *l_iter, *l_first;
40 uint i;
41 int result = 1;
42
43 STACK_INIT(loops_split, pair_split_max);
44 STACK_INIT(verts_pair, pair_split_max);
45
46 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
47 do {
48 if (BMO_vert_flag_test(bm, l_iter->v, VERT_INPUT) &&
49 /* Ensure this vertex isn't part of a contiguous group. */
50 ((BMO_vert_flag_test(bm, l_iter->prev->v, VERT_INPUT) == 0) ||
51 (BMO_vert_flag_test(bm, l_iter->next->v, VERT_INPUT) == 0)))
52 {
53 if (!l_tag_prev) {
54 l_tag_prev = l_tag_first = l_iter;
55 continue;
56 }
57
58 if (!BM_loop_is_adjacent(l_tag_prev, l_iter)) {
59 BMEdge *e;
60 e = BM_edge_exists(l_tag_prev->v, l_iter->v);
61 if (e == nullptr || !BMO_edge_flag_test(bm, e, EDGE_OUT)) {
62 BMLoop **l_pair = STACK_PUSH_RET(loops_split);
63 l_pair[0] = l_tag_prev;
64 l_pair[1] = l_iter;
65 }
66 }
67
68 l_tag_prev = l_iter;
69 }
70 } while ((l_iter = l_iter->next) != l_first);
71
72 if (STACK_SIZE(loops_split) == 0) {
73 return 0;
74 }
75
76 if (!BM_loop_is_adjacent(l_tag_first, l_tag_prev) &&
77 /* ensure we don't add the same pair twice */
78 (((loops_split[0][0] == l_tag_first) && (loops_split[0][1] == l_tag_prev)) == 0))
79 {
80 BMLoop **l_pair = STACK_PUSH_RET(loops_split);
81 l_pair[0] = l_tag_first;
82 l_pair[1] = l_tag_prev;
83 }
84
85 if (check_degenerate) {
86 BM_face_splits_check_legal(bm, f, loops_split, STACK_SIZE(loops_split));
87 }
88 else {
89 BM_face_splits_check_optimal(f, loops_split, STACK_SIZE(loops_split));
90 }
91
92 for (i = 0; i < STACK_SIZE(loops_split); i++) {
93 BMVert **v_pair;
94 if (loops_split[i][0] == nullptr) {
95 continue;
96 }
97
98 v_pair = STACK_PUSH_RET(verts_pair);
99 v_pair[0] = loops_split[i][0]->v;
100 v_pair[1] = loops_split[i][1]->v;
101 }
102
103 /* Clear and re-use to store duplicate faces, to remove after splitting is finished. */
104 STACK_CLEAR(loops_split);
105
106 for (i = 0; i < STACK_SIZE(verts_pair); i++) {
107 BMFace *f_new;
108 BMLoop *l_new;
109 BMLoop *l_pair[2];
110
111 /* Note that duplicate edges in this case is very unlikely but it can happen, see #70287. */
112 bool edge_exists = (BM_edge_exists(verts_pair[i][0], verts_pair[i][1]) != nullptr);
113 if ((l_pair[0] = BM_face_vert_share_loop(f, verts_pair[i][0])) &&
114 (l_pair[1] = BM_face_vert_share_loop(f, verts_pair[i][1])))
115 {
116 f_new = BM_face_split(bm, f, l_pair[0], l_pair[1], &l_new, nullptr, edge_exists);
117
118 /* Check if duplicate faces have been created, store the loops for removal in this case.
119 * Note that this matches how triangulate works (newly created duplicates get removed). */
120 if (UNLIKELY(edge_exists)) {
121 BMLoop **l_pair_deferred_remove = nullptr;
122 for (int j = 0; j < 2; j++) {
123 if (BM_face_find_double(l_pair[j]->f)) {
124 if (l_pair_deferred_remove == nullptr) {
125 l_pair_deferred_remove = STACK_PUSH_RET(loops_split);
126 l_pair_deferred_remove[0] = nullptr;
127 l_pair_deferred_remove[1] = nullptr;
128 }
129 l_pair_deferred_remove[j] = l_pair[j];
130 }
131 }
132 }
133 }
134 else {
135 f_new = nullptr;
136 l_new = nullptr;
137 }
138
139 if (!l_new || !f_new) {
140 result = -1;
141 break;
142 }
143
144 f = f_new;
145 // BMO_face_flag_enable(bm, f_new, FACE_NEW);
147 }
148
149 for (i = 0; i < STACK_SIZE(loops_split); i++) {
150 for (int j = 0; j < 2; j++) {
151 if (loops_split[i][j] != nullptr) {
152 BM_face_kill(bm, loops_split[i][j]->f);
153 }
154 }
155 }
156
157 return result;
158}
159
161{
162 BMOIter siter;
163 BMVert *v;
164 BMFace *f;
165 const bool check_degenerate = BMO_slot_bool_get(op->slots_in, "check_degenerate");
167
168 BLI_LINKSTACK_INIT(faces);
169
170 /* tag so we won't touch ever (typically hidden faces) */
172
173 /* add all faces connected to verts */
174 BMO_ITER (v, &siter, op->slots_in, "verts", BM_VERT) {
175 BMIter iter;
176 BMLoop *l_iter;
177
179 BM_ITER_ELEM (l_iter, &iter, v, BM_LOOPS_OF_VERT) {
180 f = l_iter->f;
182 if (!BMO_face_flag_test(bm, f, FACE_TAG)) {
184 if (f->len > 3) {
185 BLI_LINKSTACK_PUSH(faces, f);
186 }
187 }
188 }
189
190 /* flag edges even if these are not newly created
191 * this way cut-pairs that include co-linear edges will get
192 * predictable output. */
193 if (BMO_vert_flag_test(bm, l_iter->prev->v, VERT_INPUT)) {
195 }
196 if (BMO_vert_flag_test(bm, l_iter->next->v, VERT_INPUT)) {
198 }
199 }
200 }
201
202 /* connect faces */
203 while ((f = BLI_LINKSTACK_POP(faces))) {
204 if (bm_face_connect_verts(bm, f, check_degenerate) == -1) {
205 BMO_error_raise(bm, op, BMO_ERROR_FATAL, "Could not connect vertices");
206 }
207 }
208
209 BLI_LINKSTACK_FREE(faces);
210
212 bm, op, op->slots_out, "edges.out", BM_EDGE, EDGE_OUT | EDGE_OUT_ADJ);
213}
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:25
unsigned int uint
#define UNLIKELY(x)
#define STACK_CLEAR(stack)
#define STACK_DECLARE(stack)
#define STACK_SIZE(stack)
#define STACK_INIT(stack, stack_num)
#define STACK_PUSH_RET(stack)
#define BM_FACE_FIRST_LOOP(p)
void BM_face_kill(BMesh *bm, BMFace *f)
@ BMO_ERROR_FATAL
void BMO_error_raise(BMesh *bm, BMOperator *owner, eBMOpErrorLevel level, const char *msg) ATTR_NONNULL(1
#define BM_ITER_ELEM(ele, iter, data, itype)
@ BM_LOOPS_OF_VERT
ATTR_WARN_UNUSED_RESULT BMesh * bm
BMFace * BM_face_split(BMesh *bm, BMFace *f, BMLoop *l_a, BMLoop *l_b, BMLoop **r_l, BMEdge *example, const bool no_double)
Face Split.
#define BM_FACE
#define BM_EDGE
#define BM_VERT
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.
#define BMO_edge_flag_test(bm, e, oflag)
#define BMO_edge_flag_enable(bm, e, oflag)
#define BMO_vert_flag_enable(bm, e, oflag)
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)
#define BMO_ITER(ele, iter, slot_args, slot_name, restrict_flag)
#define BMO_vert_flag_test(bm, e, oflag)
#define BMO_face_flag_test(bm, e, oflag)
bool BMO_slot_bool_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
void BM_face_splits_check_optimal(BMFace *f, BMLoop *(*loops)[2], int len)
void BM_face_splits_check_legal(BMesh *bm, BMFace *f, BMLoop *(*loops)[2], int len)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
BMFace * BM_face_find_double(BMFace *f)
BMLoop * BM_face_vert_share_loop(BMFace *f, BMVert *v)
Return the Loop Shared by Face and Vertex.
BLI_INLINE bool BM_loop_is_adjacent(const BMLoop *l_a, const BMLoop *l_b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
#define FACE_EXCLUDE
#define FACE_TAG
#define EDGE_OUT_ADJ
#define EDGE_OUT
#define VERT_INPUT
void bmo_connect_verts_exec(BMesh *bm, BMOperator *op)
static int bm_face_connect_verts(BMesh *bm, BMFace *f, const bool check_degenerate)
struct BMVert * v
struct BMEdge * e
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
struct BMOpSlot slots_out[BMO_OP_MAX_SLOTS]
struct BMOpSlot slots_in[BMO_OP_MAX_SLOTS]