Blender V4.3
bmesh_path_region_uv.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
16#include "MEM_guardedalloc.h"
17
18#include "BLI_alloca.h"
19#include "BLI_linklist.h"
20#include "BLI_math_vector.h"
22
23#include "bmesh.hh"
24#include "bmesh_path_region_uv.hh" /* own include */
25
37#define USE_EDGE_CHAIN
38
39#ifdef USE_EDGE_CHAIN
45static bool bm_loop_pair_ends(BMLoop *l_pivot, BMLoop *l_end_pair[2])
46{
47 int j;
48 for (j = 0; j < 2; j++) {
49 BMLoop *l_other = j ? l_pivot->next : l_pivot->prev;
50 while (BM_vert_is_edge_pair_manifold(l_other->v)) {
51 l_other = j ? l_other->next : l_other->prev;
52 if (l_other == l_pivot) {
53 return false;
54 }
55 }
56 l_end_pair[j] = l_other;
57 }
58 BLI_assert(j == 2);
59 return true;
60}
61#endif /* USE_EDGE_CHAIN */
62
63/* -------------------------------------------------------------------- */
67static bool bm_loop_region_test(BMLoop *l, int *const depths[2], const int pass)
68{
69 const int index = BM_elem_index_get(l);
70 return (((depths[0][index] != -1) && (depths[1][index] != -1)) &&
71 ((depths[0][index] + depths[1][index]) < pass));
72}
73
74#ifdef USE_EDGE_CHAIN
75static bool bm_loop_region_test_chain(BMLoop *l, int *const depths[2], const int pass)
76{
77 BMLoop *l_end_pair[2];
78 if (bm_loop_region_test(l, depths, pass)) {
79 return true;
80 }
81 if (BM_vert_is_edge_pair_manifold(l->v) && bm_loop_pair_ends(l, l_end_pair) &&
82 bm_loop_region_test(l_end_pair[0], depths, pass) &&
83 bm_loop_region_test(l_end_pair[1], depths, pass))
84 {
85 return true;
86 }
87
88 return false;
89}
90#else
91static bool bm_loop_region_test_chain(BMLoop *l, int *const depths[2], const int pass)
92{
93 return bm_loop_region_test(l, depths, pass);
94}
95#endif
96
113 BMElem *ele_src,
114 BMElem *ele_dst,
115 const int cd_loop_uv_offset,
116 const char path_htype)
117{
118 BLI_assert(cd_loop_uv_offset >= 0);
119 int ele_loops_len[2];
120 BMLoop **ele_loops[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_loops[side] = BLI_array_alloca(ele_loops[side], f->len);
130
131 BMLoop *l_first, *l_iter;
132 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
133 do {
134 ele_loops[side][j++] = l_iter;
135 } while ((l_iter = l_iter->next) != l_first);
136 }
137 else if (ele->head.htype == BM_LOOP) {
138 if (path_htype == BM_EDGE) {
139 BMLoop *l = (BMLoop *)ele;
140 ele_loops[side] = BLI_array_alloca(ele_loops[side], 2);
141 ele_loops[side][j++] = l;
142 ele_loops[side][j++] = l->next;
143 }
144 else if (path_htype == BM_VERT) {
145 BMLoop *l = (BMLoop *)ele;
146 ele_loops[side] = BLI_array_alloca(ele_loops[side], 1);
147
148 ele_loops[side][j++] = l;
149 }
150 else {
151 BLI_assert(0);
152 }
153 }
154 else {
155 BLI_assert(0);
156 }
157 ele_loops_len[side] = j;
158 }
159
160 int *depths[2] = {nullptr};
161 int pass = 0;
162
163 BMLoop **stack = static_cast<BMLoop **>(MEM_mallocN(sizeof(*stack) * bm->totloop, __func__));
164 BMLoop **stack_other = static_cast<BMLoop **>(
165 MEM_mallocN(sizeof(*stack_other) * bm->totloop, __func__));
166
167 STACK_DECLARE(stack);
168 STACK_INIT(stack, bm->totloop);
169
170 STACK_DECLARE(stack_other);
171 STACK_INIT(stack_other, bm->totloop);
172
174
175 /* After exhausting all possible elements, we should have found all elements on the 'side_other'.
176 * otherwise, exit early. */
177 bool found_all = false;
178
179 for (int side = 0; side < 2; side++) {
180 const int side_other = !side;
181
182 /* initialize depths to -1 (un-touched), fill in with the depth as we walk over the edges. */
183 depths[side] = static_cast<int *>(MEM_mallocN(sizeof(*depths[side]) * bm->totloop, __func__));
184 copy_vn_i(depths[side], bm->totloop, -1);
185
186 /* needed for second side */
187 STACK_CLEAR(stack);
188 STACK_CLEAR(stack_other);
189
190 for (int i = 0; i < ele_loops_len[side]; i++) {
191 BMLoop *l = ele_loops[side][i];
192 depths[side][BM_elem_index_get(l)] = 0;
194 STACK_PUSH(stack, l);
195 }
196 }
197
198#ifdef USE_EDGE_CHAIN
199 /* Expand initial state to end-point vertices when they only have 2x edges,
200 * this prevents odd behavior when source or destination are in the middle
201 * of a long chain of edges. */
202 if (ELEM(path_htype, BM_VERT, BM_EDGE)) {
203 for (int i = 0; i < ele_loops_len[side]; i++) {
204 BMLoop *l = ele_loops[side][i];
205 BMLoop *l_end_pair[2];
206 if (BM_vert_is_edge_pair_manifold(l->v) && bm_loop_pair_ends(l, l_end_pair)) {
207 for (int j = 0; j < 2; j++) {
208 const int l_end_index = BM_elem_index_get(l_end_pair[j]);
209 if (depths[side][l_end_index] == -1) {
210 depths[side][l_end_index] = 0;
211 if (!BM_elem_flag_test(l_end_pair[j], BM_ELEM_TAG)) {
212 STACK_PUSH(stack, l_end_pair[j]);
213 }
214 }
215 }
216 }
217 }
218 }
219#endif /* USE_EDGE_CHAIN */
220
221 /* Keep walking over connected geometry until we find all the vertices in
222 * `ele_loops[side_other]`, or exit the loop when there's no connection. */
223 found_all = false;
224 for (pass = 1; (STACK_SIZE(stack) != 0); pass++) {
225 while (STACK_SIZE(stack) != 0) {
226 BMLoop *l_a = STACK_POP(stack);
227 const int l_a_index = BM_elem_index_get(l_a);
228
229 BMIter iter;
230 BMLoop *l_iter;
231
232 BM_ITER_ELEM (l_iter, &iter, l_a->v, BM_LOOPS_OF_VERT) {
233 if (BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
234 continue;
235 }
236 if (!BM_loop_uv_share_vert_check(l_a, l_iter, cd_loop_uv_offset)) {
237 continue;
238 }
239
240 /* Flush the depth to connected loops (only needed for UVs). */
241 if (depths[side][BM_elem_index_get(l_iter)] == -1) {
242 depths[side][BM_elem_index_get(l_iter)] = depths[side][l_a_index];
243 }
244
245 for (int j = 0; j < 2; j++) {
246 BMLoop *l_b = j ? l_iter->next : l_iter->prev;
247 int l_b_index = BM_elem_index_get(l_b);
248 if (depths[side][l_b_index] == -1) {
249
250#ifdef USE_EDGE_CHAIN
251 /* Walk along the chain, fill in values until we reach a vertex with 3+ edges. */
252 {
254 ((depths[side][l_b_index] == -1) &&
255 /* Don't walk back to the beginning */
256 (l_b != (j ? l_iter->prev : l_iter->next))))
257 {
258 depths[side][l_b_index] = pass;
259
260 l_b = j ? l_b->next : l_b->prev;
261 l_b_index = BM_elem_index_get(l_b);
262 }
263 }
264#endif /* USE_EDGE_CHAIN */
265
266 /* Add the other vertex to the stack, to be traversed in the next pass. */
267 if (depths[side][l_b_index] == -1) {
268#ifdef USE_EDGE_CHAIN
270#endif
271 BLI_assert(pass == depths[side][BM_elem_index_get(l_a)] + 1);
272 depths[side][l_b_index] = pass;
274 STACK_PUSH(stack_other, l_b);
275 }
276 }
277 }
278 }
279 }
280 }
281
282 /* Stop searching once there's none left.
283 * Note that this looks in-efficient, however until the target elements reached,
284 * it will exit immediately.
285 * After that, it takes as many passes as the element has edges to finish off. */
286 found_all = true;
287 for (int i = 0; i < ele_loops_len[side_other]; i++) {
288 if (depths[side][BM_elem_index_get(ele_loops[side_other][i])] == -1) {
289 found_all = false;
290 break;
291 }
292 }
293 if (found_all == true) {
294 pass++;
295 break;
296 }
297
298 STACK_SWAP(stack, stack_other);
299 }
300
301 /* if we have nothing left, and didn't find all elements on the other side,
302 * exit early and don't continue */
303 if (found_all == false) {
304 break;
305 }
306 }
307
308 MEM_freeN(stack);
309 MEM_freeN(stack_other);
310
311 /* Now we have depths recorded from both sides,
312 * select elements that use tagged verts. */
313 LinkNode *path = nullptr;
314
315 if (found_all == false) {
316 /* fail! (do nothing) */
317 }
318 else if (path_htype == BM_FACE) {
319 BMIter fiter;
320 BMFace *f;
321
322 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
324 /* check all verts in face are tagged */
325 BMLoop *l_first, *l_iter;
326 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
327 bool ok = true;
328#if 0
329 do {
330 if (!bm_loop_region_test_chain(l_iter->v, depths, pass)) {
331 ok = false;
332 break;
333 }
334 } while ((l_iter = l_iter->next) != l_first);
335#else
336 /* Allowing a single failure on a face gives fewer 'gaps'.
337 * While correct, in practice they're often part of what
338 * a user would consider the 'region'. */
339 int ok_tests = f->len > 3 ? 1 : 0; /* how many times we may fail */
340 do {
341 if (!bm_loop_region_test_chain(l_iter, depths, pass)) {
342 if (ok_tests == 0) {
343 ok = false;
344 break;
345 }
346 ok_tests--;
347 }
348 } while ((l_iter = l_iter->next) != l_first);
349#endif
350
351 if (ok) {
352 BLI_linklist_prepend(&path, f);
353 }
354 }
355 }
356 }
357 else if (path_htype == BM_EDGE) {
358 BMIter fiter;
359 BMFace *f;
360 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
361 BMIter liter;
362 BMLoop *l;
363 /* Check the current and next loop vertices are in the region. */
364 bool l_in_chain_next = bm_loop_region_test_chain(BM_FACE_FIRST_LOOP(f), depths, pass);
365 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
366 const bool l_in_chain = l_in_chain_next;
367 l_in_chain_next = bm_loop_region_test_chain(l->next, depths, pass);
368 if (l_in_chain && l_in_chain_next) {
369 BLI_linklist_prepend(&path, l);
370 }
371 }
372 }
373 }
374 else if (path_htype == BM_VERT) {
375 BMIter fiter;
376 BMFace *f;
377 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
378 BMIter liter;
379 BMLoop *l;
380 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
381 if (bm_loop_region_test_chain(l, depths, pass)) {
382 BLI_linklist_prepend(&path, l);
383 }
384 }
385 }
386 }
387
388 for (int side = 0; side < 2; side++) {
389 if (depths[side]) {
390 MEM_freeN(depths[side]);
391 }
392 }
393
394 return path;
395}
396
397#undef USE_EDGE_CHAIN
398
399/* -------------------------------------------------------------------- */
404 BMElem *ele_src,
405 BMElem *ele_dst,
406 const int cd_loop_uv_offset,
407 bool (*filter_fn)(BMLoop *, void *user_data),
408 void *user_data)
409{
410 LinkNode *path = nullptr;
411 /* BM_ELEM_TAG flag is used to store visited verts */
412 BMFace *f;
413 BMIter fiter;
414 int i = 0;
415
416 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
417 BMIter liter;
418 BMLoop *l;
419 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
420 BM_elem_flag_set(l, BM_ELEM_TAG, !filter_fn(l, user_data));
421 BM_elem_index_set(l, i); /* set_inline */
422 i += 1;
423 }
424 }
425 bm->elem_index_dirty &= ~BM_LOOP;
426
427 path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, cd_loop_uv_offset, BM_VERT);
428
429 return path;
430}
431
433 BMElem *ele_src,
434 BMElem *ele_dst,
435 const int cd_loop_uv_offset,
436 bool (*filter_fn)(BMLoop *, void *user_data),
437 void *user_data)
438{
439 LinkNode *path = nullptr;
440 /* BM_ELEM_TAG flag is used to store visited verts */
441 BMFace *f;
442 BMIter fiter;
443 int i = 0;
444
445 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
446 BMIter liter;
447 BMLoop *l;
448 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
449 BM_elem_flag_set(l, BM_ELEM_TAG, !filter_fn(l, user_data));
450 BM_elem_index_set(l, i); /* set_inline */
451 i += 1;
452 }
453 }
454 bm->elem_index_dirty &= ~BM_LOOP;
455
456 path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, cd_loop_uv_offset, BM_EDGE);
457
458 return path;
459}
460
462 BMElem *ele_src,
463 BMElem *ele_dst,
464 const int cd_loop_uv_offset,
465 bool (*filter_fn)(BMFace *, void *user_data),
466 void *user_data)
467{
468 LinkNode *path = nullptr;
469 /* BM_ELEM_TAG flag is used to store visited verts */
470 BMFace *f;
471 BMIter fiter;
472 int i;
473
474 /* flush flag to verts */
476
477 BM_ITER_MESH_INDEX (f, &fiter, bm, BM_FACES_OF_MESH, i) {
478 bool test;
479 BM_elem_flag_set(f, BM_ELEM_TAG, test = !filter_fn(f, user_data));
480
481 /* flush tag to verts */
482 if (test == false) {
483 BMLoop *l_first, *l_iter;
484 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
485 do {
487 } while ((l_iter = l_iter->next) != l_first);
488 }
489 }
490
491 path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, cd_loop_uv_offset, BM_FACE);
492
493 return path;
494}
495
#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.
@ BM_LOOP
@ 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_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
#define BM_ITER_MESH_INDEX(ele, iter, bm, itype, indexvar)
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_VERT
@ BM_LOOPS_OF_FACE
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
static bool bm_loop_pair_ends(BMLoop *l_pivot, BMLoop *l_end_pair[2])
static LinkNode * mesh_calc_path_region_elem(BMesh *bm, BMElem *ele_src, BMElem *ele_dst, const int cd_loop_uv_offset, const char path_htype)
LinkNode * BM_mesh_calc_path_uv_region_vert(BMesh *bm, BMElem *ele_src, BMElem *ele_dst, const int cd_loop_uv_offset, bool(*filter_fn)(BMLoop *, void *user_data), void *user_data)
LinkNode * BM_mesh_calc_path_uv_region_edge(BMesh *bm, BMElem *ele_src, BMElem *ele_dst, const int cd_loop_uv_offset, bool(*filter_fn)(BMLoop *, void *user_data), void *user_data)
LinkNode * BM_mesh_calc_path_uv_region_face(BMesh *bm, BMElem *ele_src, BMElem *ele_dst, const int cd_loop_uv_offset, bool(*filter_fn)(BMFace *, void *user_data), void *user_data)
static bool bm_loop_region_test(BMLoop *l, int *const depths[2], const int pass)
static bool bm_loop_region_test_chain(BMLoop *l, int *const depths[2], const int pass)
bool BM_vert_is_edge_pair_manifold(const BMVert *v)
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMLoop * l_b
bool BM_loop_uv_share_vert_check(BMLoop *l_a, BMLoop *l_b, const int cd_loop_uv_offset)
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 * prev
struct BMLoop * next
char elem_index_dirty
int totloop