Blender V4.3
bmesh_path_region.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
12#include "MEM_guardedalloc.h"
13
14#include "BLI_alloca.h"
15#include "BLI_linklist.h"
16#include "BLI_math_vector.h"
18
19#include "bmesh.hh"
20#include "bmesh_path_region.hh" /* own include */
21
33#define USE_EDGE_CHAIN
34
35#ifdef USE_EDGE_CHAIN
41static bool bm_vert_pair_ends(BMVert *v_pivot, BMVert *v_end_pair[2])
42{
43 BMEdge *e = v_pivot->e;
44 int j = 0;
45 do {
46 BMEdge *e_chain = e;
47 BMVert *v_other = BM_edge_other_vert(e_chain, v_pivot);
48 while (BM_vert_is_edge_pair_manifold(v_other)) {
49 BMEdge *e_chain_next = BM_DISK_EDGE_NEXT(e_chain, v_other);
50 BLI_assert(BM_DISK_EDGE_NEXT(e_chain_next, v_other) == e_chain);
51 v_other = BM_edge_other_vert(e_chain_next, v_other);
52 if (v_other == v_pivot) {
53 return false;
54 }
55 e_chain = e_chain_next;
56 }
57 v_end_pair[j++] = v_other;
58 } while ((e = BM_DISK_EDGE_NEXT(e, v_pivot)) != v_pivot->e);
59
60 BLI_assert(j == 2);
61 return true;
62}
63#endif /* USE_EDGE_CHAIN */
64
65/* -------------------------------------------------------------------- */
69static bool bm_vert_region_test(BMVert *v, int *const depths[2], const int pass)
70{
71 const int index = BM_elem_index_get(v);
72 return (((depths[0][index] != -1) && (depths[1][index] != -1)) &&
73 ((depths[0][index] + depths[1][index]) < pass));
74}
75
76#ifdef USE_EDGE_CHAIN
77static bool bm_vert_region_test_chain(BMVert *v, int *const depths[2], const int pass)
78{
79 BMVert *v_end_pair[2];
80 if (bm_vert_region_test(v, depths, pass)) {
81 return true;
82 }
84 bm_vert_region_test(v_end_pair[0], depths, pass) &&
85 bm_vert_region_test(v_end_pair[1], depths, pass))
86 {
87 return true;
88 }
89
90 return false;
91}
92#else
93static bool bm_vert_region_test_chain(BMVert *v, int *const depths[2], const int pass)
94{
95 return bm_vert_region_test(v, depths, pass);
96}
97#endif
98
115 BMElem *ele_src,
116 BMElem *ele_dst,
117 const char path_htype)
118{
119 int ele_verts_len[2];
120 BMVert **ele_verts[2];
121
122 /* Get vertices from any `ele_src/ele_dst` elements. */
123 for (int side = 0; side < 2; side++) {
124 BMElem *ele = side ? ele_dst : ele_src;
125 int j = 0;
126
127 if (ele->head.htype == BM_FACE) {
128 BMFace *f = (BMFace *)ele;
129 ele_verts[side] = BLI_array_alloca(ele_verts[side], f->len);
130
131 BMLoop *l_first, *l_iter;
132 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
133 do {
134 ele_verts[side][j++] = l_iter->v;
135 } while ((l_iter = l_iter->next) != l_first);
136 }
137 else if (ele->head.htype == BM_EDGE) {
138 BMEdge *e = (BMEdge *)ele;
139 ele_verts[side] = BLI_array_alloca(ele_verts[side], 2);
140
141 ele_verts[side][j++] = e->v1;
142 ele_verts[side][j++] = e->v2;
143 }
144 else if (ele->head.htype == BM_VERT) {
145 BMVert *v = (BMVert *)ele;
146 ele_verts[side] = BLI_array_alloca(ele_verts[side], 1);
147
148 ele_verts[side][j++] = v;
149 }
150 else {
151 BLI_assert(0);
152 }
153 ele_verts_len[side] = j;
154 }
155
156 int *depths[2] = {nullptr};
157 int pass = 0;
158
159 BMVert **stack = static_cast<BMVert **>(MEM_mallocN(sizeof(*stack) * bm->totvert, __func__));
160 BMVert **stack_other = static_cast<BMVert **>(
161 MEM_mallocN(sizeof(*stack_other) * bm->totvert, __func__));
162
163 STACK_DECLARE(stack);
164 STACK_INIT(stack, bm->totvert);
165
166 STACK_DECLARE(stack_other);
167 STACK_INIT(stack_other, bm->totvert);
168
170
171 /* After exhausting all possible elements, we should have found all elements on the 'side_other'.
172 * otherwise, exit early. */
173 bool found_all = false;
174
175 for (int side = 0; side < 2; side++) {
176 const int side_other = !side;
177
178 /* initialize depths to -1 (un-touched), fill in with the depth as we walk over the edges. */
179 depths[side] = static_cast<int *>(MEM_mallocN(sizeof(*depths[side]) * bm->totvert, __func__));
180 copy_vn_i(depths[side], bm->totvert, -1);
181
182 /* needed for second side */
183 STACK_CLEAR(stack);
184 STACK_CLEAR(stack_other);
185
186 for (int i = 0; i < ele_verts_len[side]; i++) {
187 BMVert *v = ele_verts[side][i];
188 depths[side][BM_elem_index_get(v)] = 0;
189 if (v->e && !BM_elem_flag_test(v, BM_ELEM_TAG)) {
190 STACK_PUSH(stack, v);
191 }
192 }
193
194#ifdef USE_EDGE_CHAIN
195 /* Expand initial state to end-point vertices when they only have 2x edges,
196 * this prevents odd behavior when source or destination are in the middle
197 * of a long chain of edges. */
198 if (ELEM(path_htype, BM_VERT, BM_EDGE)) {
199 for (int i = 0; i < ele_verts_len[side]; i++) {
200 BMVert *v = ele_verts[side][i];
201 BMVert *v_end_pair[2];
202 if (BM_vert_is_edge_pair_manifold(v) && bm_vert_pair_ends(v, v_end_pair)) {
203 for (int j = 0; j < 2; j++) {
204 const int v_end_index = BM_elem_index_get(v_end_pair[j]);
205 if (depths[side][v_end_index] == -1) {
206 depths[side][v_end_index] = 0;
207 if (!BM_elem_flag_test(v_end_pair[j], BM_ELEM_TAG)) {
208 STACK_PUSH(stack, v_end_pair[j]);
209 }
210 }
211 }
212 }
213 }
214 }
215#endif /* USE_EDGE_CHAIN */
216
217 /* Keep walking over connected geometry until we find all the vertices in
218 * `ele_verts[side_other]`, or exit the loop when there's no connection. */
219 found_all = false;
220 for (pass = 1; (STACK_SIZE(stack) != 0); pass++) {
221 while (STACK_SIZE(stack) != 0) {
222 BMVert *v_a = STACK_POP(stack);
223 // const int v_a_index = BM_elem_index_get(v_a); /* only for assert */
224 BMEdge *e = v_a->e;
225
226 do {
227 BMVert *v_b = BM_edge_other_vert(e, v_a);
228 int v_b_index = BM_elem_index_get(v_b);
229 if (depths[side][v_b_index] == -1) {
230
231#ifdef USE_EDGE_CHAIN
232 /* Walk along the chain, fill in values until we reach a vertex with 3+ edges. */
233 {
234 BMEdge *e_chain = e;
235 while (BM_vert_is_edge_pair_manifold(v_b) && (depths[side][v_b_index] == -1)) {
236 depths[side][v_b_index] = pass;
237
238 BMEdge *e_chain_next = BM_DISK_EDGE_NEXT(e_chain, v_b);
239 BLI_assert(BM_DISK_EDGE_NEXT(e_chain_next, v_b) == e_chain);
240 v_b = BM_edge_other_vert(e_chain_next, v_b);
241 v_b_index = BM_elem_index_get(v_b);
242 e_chain = e_chain_next;
243 }
244 }
245#endif /* USE_EDGE_CHAIN */
246
247 /* Add the other vertex to the stack, to be traversed in the next pass. */
248 if (depths[side][v_b_index] == -1) {
249#ifdef USE_EDGE_CHAIN
251#endif
252 BLI_assert(pass == depths[side][BM_elem_index_get(v_a)] + 1);
253 depths[side][v_b_index] = pass;
254 if (!BM_elem_flag_test(v_b, BM_ELEM_TAG)) {
255 STACK_PUSH(stack_other, v_b);
256 }
257 }
258 }
259 } while ((e = BM_DISK_EDGE_NEXT(e, v_a)) != v_a->e);
260 }
261
262 /* Stop searching once there's none left.
263 * Note that this looks in-efficient, however until the target elements reached,
264 * it will exit immediately.
265 * After that, it takes as many passes as the element has edges to finish off. */
266 found_all = true;
267 for (int i = 0; i < ele_verts_len[side_other]; i++) {
268 if (depths[side][BM_elem_index_get(ele_verts[side_other][i])] == -1) {
269 found_all = false;
270 break;
271 }
272 }
273 if (found_all == true) {
274 pass++;
275 break;
276 }
277
278 STACK_SWAP(stack, stack_other);
279 }
280
281 /* if we have nothing left, and didn't find all elements on the other side,
282 * exit early and don't continue */
283 if (found_all == false) {
284 break;
285 }
286 }
287
288 MEM_freeN(stack);
289 MEM_freeN(stack_other);
290
291 /* Now we have depths recorded from both sides,
292 * select elements that use tagged verts. */
293 LinkNode *path = nullptr;
294
295 if (found_all == false) {
296 /* fail! (do nothing) */
297 }
298 else if (path_htype == BM_FACE) {
299 BMIter fiter;
300 BMFace *f;
301
302 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
304 /* check all verts in face are tagged */
305 BMLoop *l_first, *l_iter;
306 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
307 bool ok = true;
308#if 0
309 do {
310 if (!bm_vert_region_test_chain(l_iter->v, depths, pass)) {
311 ok = false;
312 break;
313 }
314 } while ((l_iter = l_iter->next) != l_first);
315#else
316 /* Allowing a single failure on a face gives fewer 'gaps'.
317 * While correct, in practice they're often part of what
318 * a user would consider the 'region'. */
319 int ok_tests = f->len > 3 ? 1 : 0; /* how many times we may fail */
320 do {
321 if (!bm_vert_region_test_chain(l_iter->v, depths, pass)) {
322 if (ok_tests == 0) {
323 ok = false;
324 break;
325 }
326 ok_tests--;
327 }
328 } while ((l_iter = l_iter->next) != l_first);
329#endif
330
331 if (ok) {
332 BLI_linklist_prepend(&path, f);
333 }
334 }
335 }
336 }
337 else if (path_htype == BM_EDGE) {
338 BMIter eiter;
339 BMEdge *e;
340
341 BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
343 /* check all verts in edge are tagged */
344 bool ok = true;
345 for (int j = 0; j < 2; j++) {
346 if (!bm_vert_region_test_chain(*((&e->v1) + j), depths, pass)) {
347 ok = false;
348 break;
349 }
350 }
351
352 if (ok) {
353 BLI_linklist_prepend(&path, e);
354 }
355 }
356 }
357 }
358 else if (path_htype == BM_VERT) {
359 BMIter viter;
360 BMVert *v;
361
362 BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
363 if (bm_vert_region_test_chain(v, depths, pass)) {
364 BLI_linklist_prepend(&path, v);
365 }
366 }
367 }
368
369 for (int side = 0; side < 2; side++) {
370 if (depths[side]) {
371 MEM_freeN(depths[side]);
372 }
373 }
374
375 return path;
376}
377
378#undef USE_EDGE_CHAIN
379
380/* -------------------------------------------------------------------- */
385 BMElem *ele_src,
386 BMElem *ele_dst,
387 bool (*filter_fn)(BMVert *, void *user_data),
388 void *user_data)
389{
390 LinkNode *path = nullptr;
391 /* BM_ELEM_TAG flag is used to store visited verts */
392 BMVert *v;
393 BMIter viter;
394 int i;
395
396 BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) {
397 BM_elem_flag_set(v, BM_ELEM_TAG, !filter_fn(v, user_data));
398 BM_elem_index_set(v, i); /* set_inline */
399 }
400 bm->elem_index_dirty &= ~BM_VERT;
401
402 path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, BM_VERT);
403
404 return path;
405}
406
408 BMElem *ele_src,
409 BMElem *ele_dst,
410 bool (*filter_fn)(BMEdge *, void *user_data),
411 void *user_data)
412{
413 LinkNode *path = nullptr;
414 /* BM_ELEM_TAG flag is used to store visited verts */
415 BMEdge *e;
416 BMIter eiter;
417 int i;
418
419 /* flush flag to verts */
421
422 BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, i) {
423 bool test;
424 BM_elem_flag_set(e, BM_ELEM_TAG, test = !filter_fn(e, user_data));
425
426 /* flush tag to verts */
427 if (test == false) {
428 for (int j = 0; j < 2; j++) {
429 BM_elem_flag_disable(*((&e->v1) + j), BM_ELEM_TAG);
430 }
431 }
432 }
433
434 path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, BM_EDGE);
435
436 return path;
437}
438
440 BMElem *ele_src,
441 BMElem *ele_dst,
442 bool (*filter_fn)(BMFace *, void *user_data),
443 void *user_data)
444{
445 LinkNode *path = nullptr;
446 /* BM_ELEM_TAG flag is used to store visited verts */
447 BMFace *f;
448 BMIter fiter;
449 int i;
450
451 /* flush flag to verts */
453
454 BM_ITER_MESH_INDEX (f, &fiter, bm, BM_FACES_OF_MESH, i) {
455 bool test;
456 BM_elem_flag_set(f, BM_ELEM_TAG, test = !filter_fn(f, user_data));
457
458 /* flush tag to verts */
459 if (test == false) {
460 BMLoop *l_first, *l_iter;
461 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
462 do {
464 } while ((l_iter = l_iter->next) != l_first);
465 }
466 }
467
468 path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, BM_FACE);
469
470 return path;
471}
472
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:25
#define BLI_assert(a)
Definition BLI_assert.h:50
void copy_vn_i(int *array_tar, int size, int val)
#define ELEM(...)
#define STACK_POP(stack)
#define STACK_CLEAR(stack)
#define STACK_PUSH(stack, val)
#define STACK_DECLARE(stack)
#define STACK_SIZE(stack)
#define STACK_INIT(stack, stack_num)
#define STACK_SWAP(stack_a, stack_b)
Read Guarded memory(de)allocation.
#define BM_DISK_EDGE_NEXT(e, v)
@ BM_ELEM_TAG
#define BM_FACE_FIRST_LOOP(p)
#define BM_elem_index_get(ele)
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_flag_set(ele, hflag, val)
#define BM_elem_index_set(ele, index)
#define BM_elem_flag_test(ele, hflag)
#define BM_ITER_MESH(ele, iter, bm, itype)
#define BM_ITER_MESH_INDEX(ele, iter, bm, itype, indexvar)
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_mesh_elem_hflag_enable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
LinkNode * BM_mesh_calc_path_region_vert(BMesh *bm, BMElem *ele_src, BMElem *ele_dst, bool(*filter_fn)(BMVert *, void *user_data), void *user_data)
static LinkNode * mesh_calc_path_region_elem(BMesh *bm, BMElem *ele_src, BMElem *ele_dst, const char path_htype)
static bool bm_vert_region_test(BMVert *v, int *const depths[2], const int pass)
static bool bm_vert_pair_ends(BMVert *v_pivot, BMVert *v_end_pair[2])
static bool bm_vert_region_test_chain(BMVert *v, int *const depths[2], const int pass)
LinkNode * BM_mesh_calc_path_region_edge(BMesh *bm, BMElem *ele_src, BMElem *ele_dst, bool(*filter_fn)(BMEdge *, void *user_data), void *user_data)
LinkNode * BM_mesh_calc_path_region_face(BMesh *bm, BMElem *ele_src, BMElem *ele_dst, bool(*filter_fn)(BMFace *, void *user_data), void *user_data)
bool BM_vert_is_edge_pair_manifold(const BMVert *v)
BLI_INLINE BMVert * BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
BMHeader head
struct BMVert * v
struct BMLoop * next
struct BMEdge * e
int totvert
char elem_index_dirty