Blender V5.0
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
11
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/* -------------------------------------------------------------------- */
68
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
100
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 = MEM_malloc_arrayN<BMVert *>(bm->totvert, __func__);
160 BMVert **stack_other = MEM_malloc_arrayN<BMVert *>(bm->totvert, __func__);
161
162 STACK_DECLARE(stack);
163 STACK_INIT(stack, bm->totvert);
164
165 STACK_DECLARE(stack_other);
166 STACK_INIT(stack_other, bm->totvert);
167
169
170 /* After exhausting all possible elements, we should have found all elements on the 'side_other'.
171 * otherwise, exit early. */
172 bool found_all = false;
173
174 for (int side = 0; side < 2; side++) {
175 const int side_other = !side;
176
177 /* initialize depths to -1 (un-touched), fill in with the depth as we walk over the edges. */
178 depths[side] = static_cast<int *>(MEM_mallocN(sizeof(*depths[side]) * bm->totvert, __func__));
179 copy_vn_i(depths[side], bm->totvert, -1);
180
181 /* needed for second side */
182 STACK_CLEAR(stack);
183 STACK_CLEAR(stack_other);
184
185 for (int i = 0; i < ele_verts_len[side]; i++) {
186 BMVert *v = ele_verts[side][i];
187 depths[side][BM_elem_index_get(v)] = 0;
188 if (v->e && !BM_elem_flag_test(v, BM_ELEM_TAG)) {
189 STACK_PUSH(stack, v);
190 }
191 }
192
193#ifdef USE_EDGE_CHAIN
194 /* Expand initial state to end-point vertices when they only have 2x edges,
195 * this prevents odd behavior when source or destination are in the middle
196 * of a long chain of edges. */
197 if (ELEM(path_htype, BM_VERT, BM_EDGE)) {
198 for (int i = 0; i < ele_verts_len[side]; i++) {
199 BMVert *v = ele_verts[side][i];
200 BMVert *v_end_pair[2];
201 if (BM_vert_is_edge_pair_manifold(v) && bm_vert_pair_ends(v, v_end_pair)) {
202 for (int j = 0; j < 2; j++) {
203 const int v_end_index = BM_elem_index_get(v_end_pair[j]);
204 if (depths[side][v_end_index] == -1) {
205 depths[side][v_end_index] = 0;
206 if (!BM_elem_flag_test(v_end_pair[j], BM_ELEM_TAG)) {
207 STACK_PUSH(stack, v_end_pair[j]);
208 }
209 }
210 }
211 }
212 }
213 }
214#endif /* USE_EDGE_CHAIN */
215
216 /* Keep walking over connected geometry until we find all the vertices in
217 * `ele_verts[side_other]`, or exit the loop when there's no connection. */
218 found_all = false;
219 for (pass = 1; (STACK_SIZE(stack) != 0); pass++) {
220 while (STACK_SIZE(stack) != 0) {
221 BMVert *v_a = STACK_POP(stack);
222 // const int v_a_index = BM_elem_index_get(v_a); /* only for assert */
223 BMEdge *e = v_a->e;
224
225 do {
226 BMVert *v_b = BM_edge_other_vert(e, v_a);
227 int v_b_index = BM_elem_index_get(v_b);
228 if (depths[side][v_b_index] == -1) {
229
230#ifdef USE_EDGE_CHAIN
231 /* Walk along the chain, fill in values until we reach a vertex with 3+ edges. */
232 {
233 BMEdge *e_chain = e;
234 while (BM_vert_is_edge_pair_manifold(v_b) && (depths[side][v_b_index] == -1)) {
235 depths[side][v_b_index] = pass;
236
237 BMEdge *e_chain_next = BM_DISK_EDGE_NEXT(e_chain, v_b);
238 BLI_assert(BM_DISK_EDGE_NEXT(e_chain_next, v_b) == e_chain);
239 v_b = BM_edge_other_vert(e_chain_next, v_b);
240 v_b_index = BM_elem_index_get(v_b);
241 e_chain = e_chain_next;
242 }
243 }
244#endif /* USE_EDGE_CHAIN */
245
246 /* Add the other vertex to the stack, to be traversed in the next pass. */
247 if (depths[side][v_b_index] == -1) {
248#ifdef USE_EDGE_CHAIN
250#endif
251 BLI_assert(pass == depths[side][BM_elem_index_get(v_a)] + 1);
252 depths[side][v_b_index] = pass;
253 if (!BM_elem_flag_test(v_b, BM_ELEM_TAG)) {
254 STACK_PUSH(stack_other, v_b);
255 }
256 }
257 }
258 } while ((e = BM_DISK_EDGE_NEXT(e, v_a)) != v_a->e);
259 }
260
261 /* Stop searching once there's none left.
262 * Note that this looks in-efficient, however until the target elements reached,
263 * it will exit immediately.
264 * After that, it takes as many passes as the element has edges to finish off. */
265 found_all = true;
266 for (int i = 0; i < ele_verts_len[side_other]; i++) {
267 if (depths[side][BM_elem_index_get(ele_verts[side_other][i])] == -1) {
268 found_all = false;
269 break;
270 }
271 }
272 if (found_all == true) {
273 pass++;
274 break;
275 }
276
277 STACK_SWAP(stack, stack_other);
278 }
279
280 /* if we have nothing left, and didn't find all elements on the other side,
281 * exit early and don't continue */
282 if (found_all == false) {
283 break;
284 }
285 }
286
287 MEM_freeN(stack);
288 MEM_freeN(stack_other);
289
290 /* Now we have depths recorded from both sides,
291 * select elements that use tagged verts. */
292 LinkNode *path = nullptr;
293
294 if (found_all == false) {
295 /* fail! (do nothing) */
296 }
297 else if (path_htype == BM_FACE) {
298 BMIter fiter;
299 BMFace *f;
300
301 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
303 /* check all verts in face are tagged */
304 BMLoop *l_first, *l_iter;
305 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
306 bool ok = true;
307#if 0
308 do {
309 if (!bm_vert_region_test_chain(l_iter->v, depths, pass)) {
310 ok = false;
311 break;
312 }
313 } while ((l_iter = l_iter->next) != l_first);
314#else
315 /* Allowing a single failure on a face gives fewer 'gaps'.
316 * While correct, in practice they're often part of what
317 * a user would consider the 'region'. */
318 int ok_tests = f->len > 3 ? 1 : 0; /* how many times we may fail */
319 do {
320 if (!bm_vert_region_test_chain(l_iter->v, depths, pass)) {
321 if (ok_tests == 0) {
322 ok = false;
323 break;
324 }
325 ok_tests--;
326 }
327 } while ((l_iter = l_iter->next) != l_first);
328#endif
329
330 if (ok) {
331 BLI_linklist_prepend(&path, f);
332 }
333 }
334 }
335 }
336 else if (path_htype == BM_EDGE) {
337 BMIter eiter;
338 BMEdge *e;
339
340 BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
342 /* check all verts in edge are tagged */
343 bool ok = true;
344 for (int j = 0; j < 2; j++) {
345 if (!bm_vert_region_test_chain(*((&e->v1) + j), depths, pass)) {
346 ok = false;
347 break;
348 }
349 }
350
351 if (ok) {
352 BLI_linklist_prepend(&path, e);
353 }
354 }
355 }
356 }
357 else if (path_htype == BM_VERT) {
358 BMIter viter;
359 BMVert *v;
360
361 BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
362 if (bm_vert_region_test_chain(v, depths, pass)) {
363 BLI_linklist_prepend(&path, v);
364 }
365 }
366 }
367
368 for (int side = 0; side < 2; side++) {
369 if (depths[side]) {
370 MEM_freeN(depths[side]);
371 }
372 }
373
374 return path;
375}
376
377#undef USE_EDGE_CHAIN
378
379/* -------------------------------------------------------------------- */
382
384 BMElem *ele_src,
385 BMElem *ele_dst,
386 bool (*filter_fn)(BMVert *, void *user_data),
387 void *user_data)
388{
389 LinkNode *path = nullptr;
390 /* BM_ELEM_TAG flag is used to store visited verts */
391 BMVert *v;
392 BMIter viter;
393 int i;
394
396 BM_elem_flag_set(v, BM_ELEM_TAG, !filter_fn(v, user_data));
397 BM_elem_index_set(v, i); /* set_inline */
398 }
399 bm->elem_index_dirty &= ~BM_VERT;
400
401 path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, BM_VERT);
402
403 return path;
404}
405
407 BMElem *ele_src,
408 BMElem *ele_dst,
409 bool (*filter_fn)(BMEdge *, void *user_data),
410 void *user_data)
411{
412 LinkNode *path = nullptr;
413 /* BM_ELEM_TAG flag is used to store visited verts */
414 BMEdge *e;
415 BMIter eiter;
416 int i;
417
418 /* flush flag to verts */
420
422 bool test;
423 BM_elem_flag_set(e, BM_ELEM_TAG, test = !filter_fn(e, user_data));
424
425 /* flush tag to verts */
426 if (test == false) {
427 for (int j = 0; j < 2; j++) {
428 BM_elem_flag_disable(*((&e->v1) + j), BM_ELEM_TAG);
429 }
430 }
431 }
432
433 path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, BM_EDGE);
434
435 return path;
436}
437
439 BMElem *ele_src,
440 BMElem *ele_dst,
441 bool (*filter_fn)(BMFace *, void *user_data),
442 void *user_data)
443{
444 LinkNode *path = nullptr;
445 /* BM_ELEM_TAG flag is used to store visited verts */
446 BMFace *f;
447 BMIter fiter;
448 int i;
449
450 /* flush flag to verts */
452
453 BM_ITER_MESH_INDEX (f, &fiter, bm, BM_FACES_OF_MESH, i) {
454 bool test;
455 BM_elem_flag_set(f, BM_ELEM_TAG, test = !filter_fn(f, user_data));
456
457 /* flush tag to verts */
458 if (test == false) {
459 BMLoop *l_first, *l_iter;
460 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
461 do {
463 } while ((l_iter = l_iter->next) != l_first);
464 }
465 }
466
467 path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, BM_FACE);
468
469 return path;
470}
471
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:18
#define BLI_assert(a)
Definition BLI_assert.h:46
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)
#define BM_FACE_FIRST_LOOP(p)
@ BM_ELEM_TAG
#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
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:128
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
BMHeader head
struct BMVert * v
struct BMLoop * next
struct BMEdge * e
i
Definition text_draw.cc:230